• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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() ? 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 
Perform(public_lib::Context * const ctx,parser::Program * const program)118 bool 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         // CC-OFFNXT(G.FMT.14-CPP) project code style
129         [ctx](ir::AstNode *ast) -> ir::AstNode * {
130             if (ast->IsETSNewClassInstanceExpression()) {
131                 return ReplaceStringConstructor(ctx, ast->AsETSNewClassInstanceExpression());
132             }
133 
134             return ast;
135         },
136         Name());
137 
138     return true;
139 }
140 
141 }  // namespace ark::es2panda::compiler
142