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 #include "unionLowering.h"
17 #include "compiler/core/ASTVerifier.h"
18 #include "varbinder/variableFlags.h"
19 #include "varbinder/ETSBinder.h"
20 #include "checker/ETSchecker.h"
21 #include "checker/ets/conversion.h"
22 #include "checker/ets/boxingConverter.h"
23 #include "compiler/core/compilerContext.h"
24 #include "compiler/lowering/util.h"
25 #include "ir/base/classDefinition.h"
26 #include "ir/base/classProperty.h"
27 #include "ir/astNode.h"
28 #include "ir/expression.h"
29 #include "ir/opaqueTypeNode.h"
30 #include "ir/expressions/literals/nullLiteral.h"
31 #include "ir/expressions/literals/undefinedLiteral.h"
32 #include "ir/expressions/binaryExpression.h"
33 #include "ir/expressions/identifier.h"
34 #include "ir/expressions/memberExpression.h"
35 #include "ir/statements/blockStatement.h"
36 #include "ir/statements/classDeclaration.h"
37 #include "ir/statements/variableDeclaration.h"
38 #include "ir/ts/tsAsExpression.h"
39 #include "type_helper.h"
40
41 namespace panda::es2panda::compiler {
GetUnionFieldClass(checker::ETSChecker * checker,varbinder::VarBinder * varbinder)42 ir::ClassDefinition *GetUnionFieldClass(checker::ETSChecker *checker, varbinder::VarBinder *varbinder)
43 {
44 // Create the name for the synthetic class node
45 util::UString unionFieldClassName(util::StringView(panda_file::GetDummyClassName()), checker->Allocator());
46 varbinder::Variable *foundVar = nullptr;
47 if ((foundVar = checker->Scope()->FindLocal(unionFieldClassName.View(),
48 varbinder::ResolveBindingOptions::BINDINGS)) != nullptr) {
49 return foundVar->Declaration()->Node()->AsClassDeclaration()->Definition();
50 }
51 auto *ident = checker->AllocNode<ir::Identifier>(unionFieldClassName.View(), checker->Allocator());
52 auto [decl, var] = varbinder->NewVarDecl<varbinder::ClassDecl>(ident->Start(), ident->Name());
53 ident->SetVariable(var);
54
55 auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>(varbinder);
56 auto *classDef =
57 checker->AllocNode<ir::ClassDefinition>(checker->Allocator(), ident, ir::ClassDefinitionModifiers::GLOBAL,
58 ir::ModifierFlags::NONE, Language(Language::Id::ETS));
59 classDef->SetScope(classCtx.GetScope());
60 auto *classDecl = checker->AllocNode<ir::ClassDeclaration>(classDef, checker->Allocator());
61 classDef->Scope()->BindNode(classDecl);
62 classDef->SetTsType(checker->GlobalETSObjectType());
63 decl->BindNode(classDecl);
64 var->SetScope(classDef->Scope());
65
66 varbinder->AsETSBinder()->BuildClassDefinition(classDef);
67 return classDef;
68 }
69
CreateUnionFieldClassProperty(checker::ETSChecker * checker,varbinder::VarBinder * varbinder,checker::Type * fieldType,const util::StringView & propName)70 varbinder::LocalVariable *CreateUnionFieldClassProperty(checker::ETSChecker *checker, varbinder::VarBinder *varbinder,
71 checker::Type *fieldType, const util::StringView &propName)
72 {
73 auto *const allocator = checker->Allocator();
74 auto *const dummyClass = GetUnionFieldClass(checker, varbinder);
75 auto *classScope = dummyClass->Scope()->AsClassScope();
76
77 // Enter the union filed class instance field scope
78 auto fieldCtx = varbinder::LexicalScope<varbinder::LocalScope>::Enter(varbinder, classScope->InstanceFieldScope());
79
80 if (auto *var = classScope->FindLocal(propName, varbinder::ResolveBindingOptions::VARIABLES); var != nullptr) {
81 return var->AsLocalVariable();
82 }
83
84 // Create field name for synthetic class
85 auto *fieldIdent = allocator->New<ir::Identifier>(propName, allocator);
86
87 // Create the synthetic class property node
88 auto *field =
89 allocator->New<ir::ClassProperty>(fieldIdent, nullptr, nullptr, ir::ModifierFlags::NONE, allocator, false);
90
91 // Add the declaration to the scope
92 auto [decl, var] = varbinder->NewVarDecl<varbinder::LetDecl>(fieldIdent->Start(), fieldIdent->Name());
93 var->AddFlag(varbinder::VariableFlags::PROPERTY);
94 var->SetTsType(fieldType);
95 fieldIdent->SetVariable(var);
96 field->SetTsType(fieldType);
97 decl->BindNode(field);
98
99 ArenaVector<ir::AstNode *> fieldDecl {allocator->Adapter()};
100 fieldDecl.push_back(field);
101 dummyClass->AddProperties(std::move(fieldDecl));
102 return var->AsLocalVariable();
103 }
104
HandleUnionPropertyAccess(checker::ETSChecker * checker,varbinder::VarBinder * vbind,ir::MemberExpression * expr)105 void HandleUnionPropertyAccess(checker::ETSChecker *checker, varbinder::VarBinder *vbind, ir::MemberExpression *expr)
106 {
107 ASSERT(expr->PropVar() == nullptr);
108 expr->SetPropVar(
109 CreateUnionFieldClassProperty(checker, vbind, expr->TsType(), expr->Property()->AsIdentifier()->Name()));
110 ASSERT(expr->PropVar() != nullptr);
111 }
112
GenAsExpression(checker::ETSChecker * checker,checker::Type * const opaqueType,ir::Expression * const node,ir::AstNode * const parent)113 ir::TSAsExpression *GenAsExpression(checker::ETSChecker *checker, checker::Type *const opaqueType,
114 ir::Expression *const node, ir::AstNode *const parent)
115 {
116 auto *const typeNode = checker->AllocNode<ir::OpaqueTypeNode>(opaqueType);
117 auto *const asExpression = checker->AllocNode<ir::TSAsExpression>(node, typeNode, false);
118 asExpression->SetParent(parent);
119 node->SetParent(asExpression);
120 asExpression->Check(checker);
121 return asExpression;
122 }
123
124 /*
125 * Function that generates conversion from (union) to (primitive) type as to `as` expressions:
126 * (union) as (prim) => ((union) as (ref)) as (prim),
127 * where (ref) is some unboxable type from union constituent types.
128 * Finally, `(union) as (prim)` expression replaces union_node that came above.
129 */
UnionCastToPrimitive(checker::ETSChecker * checker,checker::ETSObjectType * unboxableRef,checker::Type * unboxedPrim,ir::Expression * unionNode)130 ir::TSAsExpression *UnionCastToPrimitive(checker::ETSChecker *checker, checker::ETSObjectType *unboxableRef,
131 checker::Type *unboxedPrim, ir::Expression *unionNode)
132 {
133 auto *const unionAsRefExpression = GenAsExpression(checker, unboxableRef, unionNode, nullptr);
134 unionAsRefExpression->SetBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxedPrim));
135 unionNode->SetParent(unionAsRefExpression);
136
137 auto *const refAsPrimExpression = GenAsExpression(checker, unboxedPrim, unionAsRefExpression, unionNode->Parent());
138 unionAsRefExpression->SetParent(refAsPrimExpression);
139
140 return refAsPrimExpression;
141 }
142
HandleUnionCastToPrimitive(checker::ETSChecker * checker,ir::TSAsExpression * expr)143 ir::TSAsExpression *HandleUnionCastToPrimitive(checker::ETSChecker *checker, ir::TSAsExpression *expr)
144 {
145 auto *const unionType = expr->Expr()->TsType()->AsETSUnionType();
146 auto *sourceType = unionType->FindExactOrBoxedType(checker, expr->TsType());
147 if (sourceType == nullptr) {
148 sourceType = unionType->AsETSUnionType()->FindTypeIsCastableToSomeType(expr->Expr(), checker->Relation(),
149 expr->TsType());
150 }
151 if (sourceType != nullptr && expr->Expr()->GetBoxingUnboxingFlags() != ir::BoxingUnboxingFlags::NONE) {
152 if (expr->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
153 auto *const asExpr = GenAsExpression(checker, sourceType, expr->Expr(), expr);
154 asExpr->SetBoxingUnboxingFlags(
155 checker->GetUnboxingFlag(checker->ETSBuiltinTypeAsPrimitiveType(sourceType)));
156 expr->Expr()->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
157 expr->SetExpr(asExpr);
158 }
159 return expr;
160 }
161 auto *const unboxableUnionType = sourceType != nullptr ? sourceType : unionType->FindUnboxableType();
162 auto *const unboxedUnionType = checker->ETSBuiltinTypeAsPrimitiveType(unboxableUnionType);
163 expr->SetExpr(UnionCastToPrimitive(checker, unboxableUnionType->AsETSObjectType(), unboxedUnionType, expr->Expr()));
164 return expr;
165 }
166
GenInstanceofExpr(checker::ETSChecker * checker,ir::Expression * unionNode,checker::Type * constituentType)167 ir::BinaryExpression *GenInstanceofExpr(checker::ETSChecker *checker, ir::Expression *unionNode,
168 checker::Type *constituentType)
169 {
170 auto *const lhsExpr = unionNode->Clone(checker->Allocator())->AsExpression();
171 lhsExpr->Check(checker);
172 lhsExpr->SetBoxingUnboxingFlags(unionNode->GetBoxingUnboxingFlags());
173 auto *rhsType = constituentType;
174 if (!constituentType->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) {
175 checker->Relation()->SetNode(unionNode);
176 rhsType = checker::conversion::Boxing(checker->Relation(), constituentType);
177 checker->Relation()->SetNode(nullptr);
178 }
179 if (constituentType->IsETSStringType()) {
180 rhsType = checker->GlobalBuiltinETSStringType();
181 }
182 ir::Expression *rhsExpr;
183 if (rhsType->IsETSUndefinedType()) {
184 rhsExpr = checker->Allocator()->New<ir::UndefinedLiteral>();
185 } else if (rhsType->IsETSNullType()) {
186 rhsExpr = checker->Allocator()->New<ir::NullLiteral>();
187 } else {
188 rhsExpr = checker->Allocator()->New<ir::Identifier>(rhsType->AsETSObjectType()->Name(), checker->Allocator());
189 auto rhsVar = NearestScope(unionNode)->Find(rhsExpr->AsIdentifier()->Name());
190 rhsExpr->AsIdentifier()->SetVariable(rhsVar.variable);
191 }
192 auto *const instanceofExpr =
193 checker->Allocator()->New<ir::BinaryExpression>(rhsExpr, rhsExpr, lexer::TokenType::KEYW_INSTANCEOF);
194 rhsExpr->SetParent(instanceofExpr);
195 rhsExpr->SetParent(instanceofExpr);
196 rhsExpr->SetTsType(rhsType);
197 instanceofExpr->SetOperationType(checker->GlobalETSObjectType());
198 instanceofExpr->SetTsType(checker->GlobalETSBooleanType());
199 return instanceofExpr;
200 }
201
GenVariableDeclForBinaryExpr(checker::ETSChecker * checker,varbinder::Scope * scope,ir::BinaryExpression * expr)202 ir::VariableDeclaration *GenVariableDeclForBinaryExpr(checker::ETSChecker *checker, varbinder::Scope *scope,
203 ir::BinaryExpression *expr)
204 {
205 ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EQUAL ||
206 expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NOT_EQUAL);
207 auto *varId = Gensym(checker->Allocator());
208 auto *var = scope->AddDecl<varbinder::LetDecl, varbinder::LocalVariable>(checker->Allocator(), varId->Name(),
209 varbinder::VariableFlags::LOCAL);
210 var->SetTsType(checker->GlobalETSBooleanType());
211 varId->SetVariable(var);
212 varId->SetTsType(var->TsType());
213
214 auto declarator = checker->AllocNode<ir::VariableDeclarator>(ir::VariableDeclaratorFlag::LET, varId);
215 ArenaVector<ir::VariableDeclarator *> declarators(checker->Allocator()->Adapter());
216 declarators.push_back(declarator);
217
218 auto varKind = ir::VariableDeclaration::VariableDeclarationKind::LET;
219 auto *binaryVarDecl =
220 checker->AllocNode<ir::VariableDeclaration>(varKind, checker->Allocator(), std::move(declarators), false);
221 binaryVarDecl->SetRange({expr->Start(), expr->End()});
222 return binaryVarDecl;
223 }
224
GenExpressionStmtWithAssignment(checker::ETSChecker * checker,ir::Identifier * varDeclId,ir::Expression * expr)225 ir::ExpressionStatement *GenExpressionStmtWithAssignment(checker::ETSChecker *checker, ir::Identifier *varDeclId,
226 ir::Expression *expr)
227 {
228 auto *assignmentForBinary =
229 checker->AllocNode<ir::AssignmentExpression>(varDeclId, expr, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
230 assignmentForBinary->SetTsType(expr->TsType());
231 return checker->AllocNode<ir::ExpressionStatement>(assignmentForBinary);
232 }
233
GenBlockStmtForAssignmentBinary(checker::ETSChecker * checker,ir::Identifier * varDeclId,ir::Expression * expr)234 ir::BlockStatement *GenBlockStmtForAssignmentBinary(checker::ETSChecker *checker, ir::Identifier *varDeclId,
235 ir::Expression *expr)
236 {
237 auto localCtx = varbinder::LexicalScope<varbinder::LocalScope>(checker->VarBinder());
238 ArenaVector<ir::Statement *> stmts(checker->Allocator()->Adapter());
239 auto *stmt = GenExpressionStmtWithAssignment(checker, varDeclId, expr);
240 stmts.push_back(stmt);
241 auto *const localBlockStmt = checker->AllocNode<ir::BlockStatement>(checker->Allocator(), std::move(stmts));
242 localBlockStmt->SetScope(localCtx.GetScope());
243 stmt->SetParent(localBlockStmt);
244 localBlockStmt->SetRange(stmt->Range());
245 localCtx.GetScope()->BindNode(localBlockStmt);
246 return localBlockStmt;
247 }
248
SetBoxFlagOrGenAsExpression(checker::ETSChecker * checker,checker::Type * constituentType,ir::Expression * otherNode)249 ir::Expression *SetBoxFlagOrGenAsExpression(checker::ETSChecker *checker, checker::Type *constituentType,
250 ir::Expression *otherNode)
251 {
252 if (constituentType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::UNBOXABLE_TYPE) &&
253 !otherNode->IsETSUnionType() && otherNode->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
254 auto *unboxedConstituentType = checker->ETSBuiltinTypeAsPrimitiveType(constituentType);
255 if (unboxedConstituentType != otherNode->TsType()) {
256 auto *const primAsExpression =
257 GenAsExpression(checker, unboxedConstituentType, otherNode, otherNode->Parent());
258 primAsExpression->SetBoxingUnboxingFlags(checker->GetBoxingFlag(constituentType));
259 return primAsExpression;
260 }
261 return otherNode;
262 }
263 if (otherNode->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
264 otherNode->SetBoxingUnboxingFlags(
265 checker->GetBoxingFlag(checker::BoxingConverter::ETSTypeFromSource(checker, otherNode->TsType())));
266 }
267 return otherNode;
268 }
269
ProcessOperandsInBinaryExpr(checker::ETSChecker * checker,ir::BinaryExpression * expr,checker::Type * constituentType)270 ir::Expression *ProcessOperandsInBinaryExpr(checker::ETSChecker *checker, ir::BinaryExpression *expr,
271 checker::Type *constituentType)
272 {
273 ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EQUAL ||
274 expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NOT_EQUAL);
275 bool isLhsUnion = false;
276 ir::Expression *unionNode = (isLhsUnion = expr->Left()->TsType()->IsETSUnionType()) ? expr->Left() : expr->Right();
277 checker::Type *typeToCast = constituentType->IsETSNullLike()
278 ? unionNode->TsType()->AsETSUnionType()->GetLeastUpperBoundType()
279 : constituentType;
280 auto *const asExpression = GenAsExpression(checker, typeToCast, unionNode, expr);
281 if (isLhsUnion) {
282 expr->SetLeft(asExpression);
283 expr->SetRight(SetBoxFlagOrGenAsExpression(checker, constituentType, expr->Right()));
284 } else {
285 expr->SetRight(asExpression);
286 expr->SetLeft(SetBoxFlagOrGenAsExpression(checker, constituentType, expr->Left()));
287 }
288 expr->SetOperationType(checker->GlobalETSObjectType());
289 expr->SetTsType(checker->GlobalETSBooleanType());
290 return expr;
291 }
292
FindStatementFromNode(ir::Expression * expr)293 ir::Statement *FindStatementFromNode(ir::Expression *expr)
294 {
295 ir::AstNode *node = expr;
296 while (!node->IsStatement()) {
297 node = node->Parent();
298 }
299 ASSERT(node->IsStatement());
300 return node->AsStatement();
301 }
302
InsertInstanceofTreeBeforeStmt(ir::Statement * stmt,ir::VariableDeclaration * binaryVarDecl,ir::Statement * instanceofTree)303 void InsertInstanceofTreeBeforeStmt(ir::Statement *stmt, ir::VariableDeclaration *binaryVarDecl,
304 ir::Statement *instanceofTree)
305 {
306 if (stmt->IsVariableDeclarator()) {
307 ASSERT(stmt->Parent()->IsVariableDeclaration());
308 stmt = stmt->Parent()->AsVariableDeclaration();
309 }
310 ASSERT(stmt->Parent()->IsBlockStatement());
311 auto *block = stmt->Parent()->AsBlockStatement();
312 binaryVarDecl->SetParent(block);
313 instanceofTree->SetParent(block);
314 auto itStmt = std::find(block->Statements().begin(), block->Statements().end(), stmt);
315 block->Statements().insert(itStmt, {binaryVarDecl, instanceofTree});
316 }
317
ReplaceBinaryExprInStmt(checker::ETSChecker * checker,ir::Expression * unionNode,ir::BlockStatement * block,ir::BinaryExpression * expr)318 ir::BlockStatement *ReplaceBinaryExprInStmt(checker::ETSChecker *checker, ir::Expression *unionNode,
319 ir::BlockStatement *block, ir::BinaryExpression *expr)
320 {
321 auto *stmt = FindStatementFromNode(expr);
322 ASSERT(stmt->IsVariableDeclarator() || block == stmt->Parent()); // statement with union
323 auto *const binaryVarDecl = GenVariableDeclForBinaryExpr(checker, NearestScope(stmt), expr);
324 auto *const varDeclId = binaryVarDecl->Declarators().front()->Id(); // only one declarator was generated
325 ir::IfStatement *instanceofTree = nullptr;
326 for (auto *uType : unionNode->TsType()->AsETSUnionType()->ConstituentTypes()) {
327 auto *const test = GenInstanceofExpr(checker, unionNode, uType);
328 auto *clonedBinary = expr->Clone(checker->Allocator(), expr->Parent())->AsBinaryExpression();
329 clonedBinary->Check(checker);
330 auto *const consequent = GenBlockStmtForAssignmentBinary(
331 checker, varDeclId->AsIdentifier(), ProcessOperandsInBinaryExpr(checker, clonedBinary, uType));
332 instanceofTree = checker->Allocator()->New<ir::IfStatement>(test, consequent, instanceofTree);
333 test->SetParent(instanceofTree);
334 consequent->SetParent(instanceofTree);
335 if (instanceofTree->Alternate() != nullptr) {
336 instanceofTree->Alternate()->SetParent(instanceofTree);
337 }
338 }
339 ASSERT(instanceofTree != nullptr);
340 // Replacing a binary expression with an identifier
341 // that was set in one of the branches of the `instanceof_tree` tree
342 stmt->TransformChildrenRecursively([varDeclId](ir::AstNode *ast) -> ir::AstNode * {
343 if (ast->IsBinaryExpression() && ast->AsBinaryExpression()->OperationType() != nullptr &&
344 ast->AsBinaryExpression()->OperationType()->IsETSUnionType()) {
345 return varDeclId;
346 }
347
348 return ast;
349 });
350 InsertInstanceofTreeBeforeStmt(stmt, binaryVarDecl, instanceofTree);
351 return block;
352 }
353
HandleBlockWithBinaryAndUnion(checker::ETSChecker * checker,ir::BlockStatement * block,ir::BinaryExpression * binExpr)354 ir::BlockStatement *HandleBlockWithBinaryAndUnion(checker::ETSChecker *checker, ir::BlockStatement *block,
355 ir::BinaryExpression *binExpr)
356 {
357 if (binExpr->OperatorType() != lexer::TokenType::PUNCTUATOR_EQUAL &&
358 binExpr->OperatorType() != lexer::TokenType::PUNCTUATOR_NOT_EQUAL) {
359 checker->ThrowTypeError("Bad operand type, unions are not allowed in binary expressions except equality.",
360 binExpr->Start());
361 }
362 ir::Expression *unionNode = binExpr->Left()->TsType()->IsETSUnionType() ? binExpr->Left() : binExpr->Right();
363 return ReplaceBinaryExprInStmt(checker, unionNode, block, binExpr);
364 }
365
HandleBlockWithBinaryAndUnions(checker::ETSChecker * checker,ir::BlockStatement * block,const ir::NodePredicate & handleBinary)366 ir::BlockStatement *HandleBlockWithBinaryAndUnions(checker::ETSChecker *checker, ir::BlockStatement *block,
367 const ir::NodePredicate &handleBinary)
368 {
369 ir::BlockStatement *modifiedAstBlock = block;
370 while (modifiedAstBlock->IsAnyChild(handleBinary)) {
371 modifiedAstBlock = HandleBlockWithBinaryAndUnion(
372 checker, modifiedAstBlock, modifiedAstBlock->FindChild(handleBinary)->AsBinaryExpression());
373 }
374 return modifiedAstBlock;
375 }
376
Perform(public_lib::Context * ctx,parser::Program * program)377 bool UnionLowering::Perform(public_lib::Context *ctx, parser::Program *program)
378 {
379 for (auto &[_, ext_programs] : program->ExternalSources()) {
380 (void)_;
381 for (auto *extProg : ext_programs) {
382 Perform(ctx, extProg);
383 }
384 }
385
386 checker::ETSChecker *checker = ctx->checker->AsETSChecker();
387
388 program->Ast()->TransformChildrenRecursively([checker](ir::AstNode *ast) -> ir::AstNode * {
389 if (ast->IsMemberExpression() && ast->AsMemberExpression()->Object()->TsType() != nullptr &&
390 ast->AsMemberExpression()->Object()->TsType()->IsETSUnionType()) {
391 HandleUnionPropertyAccess(checker, checker->VarBinder(), ast->AsMemberExpression());
392 return ast;
393 }
394
395 if (ast->IsTSAsExpression() && ast->AsTSAsExpression()->Expr()->TsType() != nullptr &&
396 ast->AsTSAsExpression()->Expr()->TsType()->IsETSUnionType() &&
397 ast->AsTSAsExpression()->TsType() != nullptr &&
398 ast->AsTSAsExpression()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
399 return HandleUnionCastToPrimitive(checker, ast->AsTSAsExpression());
400 }
401
402 auto handleBinary = [](const ir::AstNode *astNode) {
403 return astNode->IsBinaryExpression() && astNode->AsBinaryExpression()->OperationType() != nullptr &&
404 astNode->AsBinaryExpression()->OperationType()->IsETSUnionType();
405 };
406 if (ast->IsBlockStatement() && ast->IsAnyChild(handleBinary)) {
407 return HandleBlockWithBinaryAndUnions(checker, ast->AsBlockStatement(), handleBinary);
408 }
409
410 return ast;
411 });
412
413 return true;
414 }
415
Postcondition(public_lib::Context * ctx,const parser::Program * program)416 bool UnionLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program)
417 {
418 bool current = !program->Ast()->IsAnyChild([](const ir::AstNode *ast) {
419 return ast->IsMemberExpression() && ast->AsMemberExpression()->Object()->TsType() != nullptr &&
420 ast->AsMemberExpression()->Object()->TsType()->IsETSUnionType() &&
421 ast->AsMemberExpression()->PropVar() == nullptr;
422 });
423 if (!current || ctx->compilerContext->Options()->compilationMode != CompilationMode::GEN_STD_LIB) {
424 return current;
425 }
426
427 for (auto &[_, ext_programs] : program->ExternalSources()) {
428 (void)_;
429 for (auto *extProg : ext_programs) {
430 if (!Postcondition(ctx, extProg)) {
431 return false;
432 }
433 }
434 }
435 return true;
436 }
437
438 } // namespace panda::es2panda::compiler
439