1 /**
2 * Copyright (c) 2024-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 #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
25 namespace ark::es2panda::compiler {
26
Name() const27 std::string_view StringConstructorLowering::Name() const
28 {
29 return "StringConstructorLowering";
30 }
31
32 // NOLINTBEGIN(modernize-avoid-c-arrays)
33 static constexpr char const FORMAT_CHECK_NULL_EXPRESSION[] =
34 "let @@I1 = (@@E2);"
35 "(@@I3 === null ? \"null\" : @@I4 as String)";
36
37 static constexpr char const FORMAT_CHECK_UNDEFINED_EXPRESSION[] =
38 "let @@I1 = (@@E2);"
39 "(@@I3 === undefined ? \"undefined\" : @@I4 as String)";
40
41 static constexpr char const FORMAT_CHECK_NULLISH_EXPRESSION[] =
42 "let @@I1 = (@@E2);"
43 "(@@I3 instanceof null ? \"null\" : (@@I4 instanceof undefined ? \"undefined\" : @@I5 as String))";
44
45 static constexpr char const FORMAT_TO_STRING_EXPRESSION[] = "((@@E1 as Object).toString())";
46 // NOLINTEND(modernize-avoid-c-arrays)
47
ReplaceStringConstructor(public_lib::Context * const ctx,ir::ETSNewClassInstanceExpression * newClassInstExpr)48 ir::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() ? ctx->AllocNode<ir::StringLiteral>("null")
74 : ctx->AllocNode<ir::StringLiteral>("undefined");
75 ES2PANDA_ASSERT(literal != nullptr);
76 literal->SetParent(newClassInstExpr->Parent());
77
78 // Run checker
79 literal->Check(checker);
80 return literal;
81 }
82
83 // Enter to the old scope
84 auto *scope = NearestScope(newClassInstExpr);
85 auto exprCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
86
87 // Generate temporary variable
88 auto const tmpIdentName = GenName(ctx->Allocator());
89
90 // Create BlockExpression
91 ir::Expression *blockExpr = nullptr;
92 if (argType->PossiblyETSNull() && !argType->PossiblyETSUndefined()) {
93 blockExpr = parser->CreateFormattedExpression(FORMAT_CHECK_NULL_EXPRESSION, tmpIdentName, arg, tmpIdentName,
94 tmpIdentName);
95 } else if (argType->PossiblyETSUndefined() && !argType->PossiblyETSNull()) {
96 blockExpr = parser->CreateFormattedExpression(FORMAT_CHECK_UNDEFINED_EXPRESSION, tmpIdentName, arg,
97 tmpIdentName, tmpIdentName);
98 } else if (argType->PossiblyETSNullish()) {
99 blockExpr = parser->CreateFormattedExpression(FORMAT_CHECK_NULLISH_EXPRESSION, tmpIdentName, arg,
100 tmpIdentName, tmpIdentName, tmpIdentName);
101 } else {
102 blockExpr = parser->CreateFormattedExpression(FORMAT_TO_STRING_EXPRESSION, arg);
103 }
104
105 blockExpr->SetParent(newClassInstExpr->Parent());
106
107 // Run VarBinder for new BlockExpression
108 InitScopesPhaseETS::RunExternalNode(blockExpr, checker->VarBinder());
109 checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(blockExpr, NearestScope(blockExpr));
110
111 // Run checker
112 blockExpr->Check(checker);
113 return blockExpr;
114 }
115
116 return newClassInstExpr;
117 }
118
PerformForModule(public_lib::Context * const ctx,parser::Program * const program)119 bool StringConstructorLowering::PerformForModule(public_lib::Context *const ctx, parser::Program *const program)
120 {
121 program->Ast()->TransformChildrenRecursively(
122 // CC-OFFNXT(G.FMT.14-CPP) project code style
123 [ctx](ir::AstNode *ast) -> ir::AstNode * {
124 if (ast->IsETSNewClassInstanceExpression()) {
125 return ReplaceStringConstructor(ctx, ast->AsETSNewClassInstanceExpression());
126 }
127
128 return ast;
129 },
130 Name());
131
132 return true;
133 }
134
135 } // namespace ark::es2panda::compiler
136