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 "optionalLowering.h" 17#include "checker/ETSchecker.h" 18#include "compiler/lowering/util.h" 19#include "compiler/lowering/scopesInit/scopesInitPhase.h" 20#include "ir/statements/blockStatement.h" 21#include "ir/expressions/memberExpression.h" 22#include "parser/ETSparser.h" 23#include "varbinder/ETSBinder.h" 24 25namespace ark::es2panda::compiler { 26 27std::string_view OptionalLowering::Name() const 28{ 29 return "OptionalLowering"; 30} 31 32static ir::AstNode *RefineSourceRanges(ir::AstNode *node) 33{ 34 auto const isDummyLoc = [](lexer::SourceRange const &range) { 35 return range.start.index == 0 && range.start.line == 0; 36 }; 37 38 auto const refine = [isDummyLoc](ir::AstNode *n) { 39 if (isDummyLoc(n->Range())) { 40 n->SetRange(n->Parent()->Range()); 41 }; 42 }; 43 44 refine(node); 45 node->IterateRecursively(refine); 46 return node; 47} 48 49template <typename Expr, typename GetSource, typename SetSource> 50static ir::AstNode *LowerOptionalExpr(GetSource const &getSource, SetSource const &setSource, public_lib::Context *ctx, 51 Expr *const expr, ir::ChainExpression *const chain) 52{ 53 auto *const allocator = ctx->allocator; 54 auto *const parser = ctx->parser->AsETSParser(); 55 auto *const varbinder = ctx->parserProgram->VarBinder(); 56 57 auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, NearestScope(expr)); 58 auto *tmpIdent = Gensym(allocator); 59 auto *tmpIdentClone = tmpIdent->Clone(allocator, nullptr); 60 tmpIdentClone->SetReference(); 61 62 // '0's act as placeholders 63 auto *sequenceExpr = parser->CreateFormattedExpression( 64 "let @@I1 = 0;" 65 "(@@I2 == null ? undefined : 0);", 66 tmpIdent, tmpIdentClone); 67 sequenceExpr->SetParent(chain->Parent()); 68 InitScopesPhaseETS::RunExternalNode(sequenceExpr, ctx->parserProgram->VarBinder()); 69 70 auto const &stmts = sequenceExpr->AsBlockExpression()->Statements(); 71 stmts[0]->AsVariableDeclaration()->Declarators()[0]->SetInit(getSource(expr)); 72 stmts[1]->AsExpressionStatement()->GetExpression()->AsConditionalExpression()->SetAlternate(chain->GetExpression()); 73 74 setSource(expr, parser->CreateFormattedExpression("@@I1", tmpIdentClone->Clone(allocator, nullptr))); 75 return sequenceExpr; 76} 77 78static ir::AstNode *LowerExpression(public_lib::Context *ctx, ir::MemberExpression *const expr, 79 ir::ChainExpression *chain) 80{ 81 ASSERT(expr->IsOptional()); 82 expr->ClearOptional(); 83 return LowerOptionalExpr<ir::MemberExpression>([](auto *e) { return e->Object(); }, 84 [](auto *e, auto *obj) { e->SetObject(obj); }, ctx, expr, chain); 85} 86 87static ir::AstNode *LowerExpression(public_lib::Context *ctx, ir::CallExpression *const expr, 88 ir::ChainExpression *chain) 89{ 90 ASSERT(expr->IsOptional()); 91 expr->ClearOptional(); 92 return LowerOptionalExpr<ir::CallExpression>([](auto *e) { return e->Callee(); }, 93 [](auto *e, auto *callee) { e->SetCallee(callee); }, ctx, expr, chain); 94} 95 96static ir::Expression *FindOptionalInChain(ir::Expression *expr) 97{ 98 if (expr->IsMemberExpression()) { 99 auto typed = expr->AsMemberExpression(); 100 return typed->IsOptional() ? typed : FindOptionalInChain(typed->Object()); 101 } 102 if (expr->IsCallExpression()) { 103 auto typed = expr->AsCallExpression(); 104 return typed->IsOptional() ? typed : FindOptionalInChain(typed->Callee()); 105 } 106 if (expr->IsTSNonNullExpression()) { 107 return FindOptionalInChain(expr->AsTSNonNullExpression()->Expr()); 108 } 109 UNREACHABLE(); 110} 111 112static ir::AstNode *LowerChain(public_lib::Context *ctx, ir::ChainExpression *const chain) 113{ 114 auto optional = FindOptionalInChain(chain->GetExpression()); 115 if (optional->IsMemberExpression()) { 116 return LowerExpression(ctx, optional->AsMemberExpression(), chain); 117 } 118 if (optional->IsCallExpression()) { 119 return LowerExpression(ctx, optional->AsCallExpression(), chain); 120 } 121 UNREACHABLE(); 122} 123 124bool OptionalLowering::Perform(public_lib::Context *ctx, parser::Program *program) 125{ 126 for (auto &[_, ext_programs] : program->ExternalSources()) { 127 (void)_; 128 for (auto *extProg : ext_programs) { 129 Perform(ctx, extProg); 130 } 131 } 132 133 program->Ast()->TransformChildrenRecursively( 134 [ctx](ir::AstNode *const node) -> ir::AstNode * { 135 if (node->IsChainExpression()) { 136 return RefineSourceRanges(LowerChain(ctx, node->AsChainExpression())); 137 } 138 return node; 139 }, 140 Name()); 141 142 return true; 143} 144 145bool OptionalLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program) 146{ 147 for (auto &[_, ext_programs] : program->ExternalSources()) { 148 (void)_; 149 for (auto *extProg : ext_programs) { 150 if (!Postcondition(ctx, extProg)) { 151 return false; 152 } 153 } 154 } 155 156 return !program->Ast()->IsAnyChild([](const ir::AstNode *node) { 157 return node->IsChainExpression() || (node->IsMemberExpression() && node->AsMemberExpression()->IsOptional()) || 158 (node->IsCallExpression() && node->AsCallExpression()->IsOptional()); 159 }); 160} 161 162} // namespace ark::es2panda::compiler 163