1/** 2 * Copyright (c) 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 "stringConstructorLowering.h" 17 18#include "checker/ETSchecker.h" 19#include "compiler/lowering/util.h" 20#include "compiler/lowering/scopesInit/scopesInitPhase.h" 21#include "parser/ETSparser.h" 22#include "varbinder/ETSBinder.h" 23#include "varbinder/scope.h" 24 25namespace ark::es2panda::compiler { 26 27std::string_view StringConstructorLowering::Name() const 28{ 29 return "StringConstructorLowering"; 30} 31 32// NOLINTBEGIN(modernize-avoid-c-arrays) 33static constexpr char const FORMAT_CHECK_NULL_EXPRESSION[] = 34 "let @@I1 = (@@E2);" 35 "(@@I3 === null ? \"null\" : @@I4 as String)"; 36 37static constexpr char const FORMAT_CHECK_UNDEFINED_EXPRESSION[] = 38 "let @@I1 = (@@E2);" 39 "(@@I3 === undefined ? \"undefined\" : @@I4 as String)"; 40 41static constexpr char const FORMAT_CHECK_NULLISH_EXPRESSION[] = 42 "let @@I1 = (@@E2);" 43 "(@@I3 instanceof null ? \"null\" : (@@I4 instanceof undefined ? \"undefined\" : @@I5 as String))"; 44 45static constexpr char const FORMAT_TO_STRING_EXPRESSION[] = "((@@E1 as Object).toString())"; 46// NOLINTEND(modernize-avoid-c-arrays) 47 48ir::Expression *ReplaceStringConstructor(public_lib::Context *const ctx, 49 ir::ETSNewClassInstanceExpression *newClassInstExpr) 50{ 51 auto *checker = ctx->checker->AsETSChecker(); 52 auto *parser = ctx->parser->AsETSParser(); 53 54 // Skip missing signatures 55 if (newClassInstExpr->GetSignature() == nullptr || newClassInstExpr->GetSignature()->InternalName() == nullptr) { 56 return newClassInstExpr; 57 } 58 59 // Case for the constructor: new String(str: string) 60 if (newClassInstExpr->GetSignature()->InternalName() == Signatures::BUILTIN_STRING_FROM_STRING_CTOR) { 61 auto *arg = newClassInstExpr->GetArguments()[0]; 62 arg->SetParent(newClassInstExpr->Parent()); 63 return arg; 64 } 65 66 // Case for the constructor: new String(str: NullishType) 67 if (newClassInstExpr->GetSignature()->InternalName() == Signatures::BUILTIN_STRING_FROM_NULLISH_CTOR) { 68 auto *arg = newClassInstExpr->GetArguments()[0]; 69 auto *argType = arg->TsType(); 70 71 // For the case when the constructor parameter is "null" or "undefined" 72 if (argType->IsETSNullType() || argType->IsETSUndefinedType()) { 73 auto *literal = argType->IsETSNullType() ? checker->AllocNode<ir::StringLiteral>("null") 74 : checker->AllocNode<ir::StringLiteral>("undefined"); 75 literal->SetParent(newClassInstExpr->Parent()); 76 77 // Run checker 78 literal->Check(checker); 79 return literal; 80 } 81 82 // Enter to the old scope 83 auto *scope = NearestScope(newClassInstExpr); 84 auto exprCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope); 85 86 // Generate temporary variable 87 auto const tmpIdentName = GenName(checker->Allocator()); 88 89 // Create BlockExpression 90 ir::Expression *blockExpr = nullptr; 91 if (argType->PossiblyETSNull() && !argType->PossiblyETSUndefined()) { 92 blockExpr = parser->CreateFormattedExpression(FORMAT_CHECK_NULL_EXPRESSION, tmpIdentName, arg, tmpIdentName, 93 tmpIdentName); 94 } else if (argType->PossiblyETSUndefined() && !argType->PossiblyETSNull()) { 95 blockExpr = parser->CreateFormattedExpression(FORMAT_CHECK_UNDEFINED_EXPRESSION, tmpIdentName, arg, 96 tmpIdentName, tmpIdentName); 97 } else if (argType->PossiblyETSNullish()) { 98 blockExpr = parser->CreateFormattedExpression(FORMAT_CHECK_NULLISH_EXPRESSION, tmpIdentName, arg, 99 tmpIdentName, tmpIdentName, tmpIdentName); 100 } else { 101 blockExpr = parser->CreateFormattedExpression(FORMAT_TO_STRING_EXPRESSION, arg); 102 } 103 104 blockExpr->SetParent(newClassInstExpr->Parent()); 105 106 // Run VarBinder for new BlockExpression 107 InitScopesPhaseETS::RunExternalNode(blockExpr, checker->VarBinder()); 108 checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(blockExpr, NearestScope(blockExpr)); 109 110 // Run checker 111 blockExpr->Check(checker); 112 return blockExpr; 113 } 114 115 return newClassInstExpr; 116} 117 118bool StringConstructorLowering::Perform(public_lib::Context *const ctx, parser::Program *const program) 119{ 120 for (const auto &[_, ext_programs] : program->ExternalSources()) { 121 (void)_; 122 for (auto *const extProg : ext_programs) { 123 Perform(ctx, extProg); 124 } 125 } 126 127 program->Ast()->TransformChildrenRecursively( 128 [ctx](ir::AstNode *ast) -> ir::AstNode * { 129 if (ast->IsETSNewClassInstanceExpression()) { 130 return ReplaceStringConstructor(ctx, ast->AsETSNewClassInstanceExpression()); 131 } 132 133 return ast; 134 }, 135 Name()); 136 137 return true; 138} 139 140} // namespace ark::es2panda::compiler 141