1 /**
2 * Copyright (c) 2023-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 "promiseVoid.h"
17 #include "checker/ETSchecker.h"
18 #include "checker/checker.h"
19 #include "generated/signatures.h"
20 #include "ir/base/scriptFunction.h"
21 #include "ir/ets/etsTypeReference.h"
22 #include "ir/ets/etsTypeReferencePart.h"
23 #include "ir/expressions/functionExpression.h"
24 #include "ir/expressions/identifier.h"
25 #include "ir/statements/returnStatement.h"
26 #include "ir/typeNode.h"
27 #include "lexer/token/sourceLocation.h"
28 #include "ir/astNode.h"
29 #include "ir/statements/blockStatement.h"
30 #include "util/ustring.h"
31
32 namespace ark::es2panda::compiler {
HandleAsyncScriptFunctionBody(checker::ETSChecker * checker,ir::BlockStatement * body)33 ir::BlockStatement *PromiseVoidInferencePhase::HandleAsyncScriptFunctionBody(checker::ETSChecker *checker,
34 ir::BlockStatement *body)
35 {
36 (void)checker;
37 body->TransformChildrenRecursively(
38 [checker](ir::AstNode *ast) -> ir::AstNode * {
39 if (ast->IsReturnStatement()) {
40 auto *returnStmt = ast->AsReturnStatement();
41 const auto *arg = returnStmt->Argument();
42 if (arg == nullptr) {
43 auto *voidId =
44 checker->AllocNode<ir::Identifier>(compiler::Signatures::UNDEFINED, checker->Allocator());
45 const auto &returnLoc = returnStmt->Range();
46 voidId->SetRange({returnLoc.end, returnLoc.end});
47 returnStmt->SetArgument(voidId);
48 }
49 }
50 return ast;
51 },
52 Name());
53
54 return body;
55 }
56
SetRangeRecursively(ir::TypeNode * node,const lexer::SourceRange & loc)57 void PromiseVoidInferencePhase::SetRangeRecursively(ir::TypeNode *node, const lexer::SourceRange &loc)
58 {
59 node->SetRange(loc);
60 node->TransformChildrenRecursively(
61 [loc](ir::AstNode *ast) -> ir::AstNode * {
62 ast->SetRange(loc);
63 return ast;
64 },
65 Name());
66 }
67
CreatePromiseVoidType(checker::ETSChecker * checker,const lexer::SourceRange & loc)68 ir::TypeNode *PromiseVoidInferencePhase::CreatePromiseVoidType(checker::ETSChecker *checker,
69 const lexer::SourceRange &loc)
70 {
71 auto *voidParam = [checker]() {
72 auto paramsVector = ArenaVector<ir::TypeNode *>(checker->Allocator()->Adapter());
73 auto *voidId = checker->AllocNode<ir::Identifier>(compiler::Signatures::UNDEFINED, checker->Allocator());
74 voidId->SetReference();
75 auto *part = checker->AllocNode<ir::ETSTypeReferencePart>(voidId);
76 paramsVector.push_back(checker->AllocNode<ir::ETSTypeReference>(part));
77 auto *params = checker->AllocNode<ir::TSTypeParameterInstantiation>(std::move(paramsVector));
78 return params;
79 }();
80
81 auto *promiseVoidType = [checker, voidParam]() {
82 auto *promiseId =
83 checker->AllocNode<ir::Identifier>(compiler::Signatures::BUILTIN_PROMISE_CLASS, checker->Allocator());
84 promiseId->SetReference();
85 auto *part = checker->AllocNode<ir::ETSTypeReferencePart>(promiseId, voidParam, nullptr);
86 auto *type = checker->AllocNode<ir::ETSTypeReference>(part);
87 return type;
88 }();
89
90 SetRangeRecursively(promiseVoidType, loc);
91
92 return promiseVoidType;
93 }
94
CheckForPromiseVoid(const ir::TypeNode * type)95 static bool CheckForPromiseVoid(const ir::TypeNode *type)
96 {
97 if (type == nullptr || !type->IsETSTypeReference()) {
98 return false;
99 }
100
101 auto *typeRef = type->AsETSTypeReference();
102 auto *typePart = typeRef->Part();
103 if (typePart->Previous() != nullptr) {
104 return false;
105 }
106
107 if (typePart->TypeParams() == nullptr) {
108 return false;
109 }
110 const auto ¶ms = typePart->TypeParams()->Params();
111 if (params.size() != 1) {
112 return false;
113 }
114
115 const auto ¶m = params.at(0);
116 if (!param->IsETSTypeReference()) {
117 return false;
118 }
119
120 const auto *paramRef = param->AsETSTypeReference();
121 const auto *paramPart = paramRef->Part();
122 if (paramPart->Previous() != nullptr) {
123 return false;
124 }
125
126 const auto isTypePromise = typePart->Name()->AsIdentifier()->Name() == compiler::Signatures::BUILTIN_PROMISE_CLASS;
127 const auto isParamVoid = paramPart->Name()->AsIdentifier()->Name() == compiler::Signatures::UNDEFINED;
128
129 return isTypePromise && isParamVoid;
130 }
131
132 using AstNodePtr = ir::AstNode *;
133
134 /*
135 * Transformation is basically syntactical: it adds relevant return type and return statements to methods and function
136 * NOTE: but not for lambdas, at least for now
137 * So, the code
138 * async function f() {}
139 * transforms to
140 * async function f(): Promise<void> { return Void; }
141 * */
142
Perform(public_lib::Context * ctx,parser::Program * program)143 bool PromiseVoidInferencePhase::Perform(public_lib::Context *ctx, parser::Program *program)
144 {
145 auto *checker = ctx->checker->AsETSChecker();
146
147 auto genTypeLocation = [](ir::ScriptFunction *function) -> lexer::SourceRange {
148 const auto ¶ms = function->Params();
149 const auto &id = function->Id();
150 const auto &body = function->Body();
151 if (!params.empty()) {
152 const auto &last = params.back();
153 const auto &loc = last->Range();
154 return {loc.end, loc.end};
155 }
156
157 if (id != nullptr) {
158 const auto &loc = id->Range();
159 return {loc.end, loc.end};
160 }
161
162 if (function->HasBody()) {
163 const auto &loc = body->Range();
164 return {loc.start, loc.start};
165 }
166
167 const auto &loc = function->Range();
168 return {loc.end, loc.end};
169 };
170
171 const auto transformer = [this, checker, genTypeLocation](ir::AstNode *ast) -> AstNodePtr {
172 if (!(ast->IsScriptFunction() && ast->AsScriptFunction()->IsAsyncFunc())) {
173 return ast;
174 }
175
176 auto *function = ast->AsScriptFunction();
177 auto *returnAnn = function->ReturnTypeAnnotation();
178 const auto hasReturnAnn = returnAnn != nullptr;
179 const auto hasPromiseVoid = CheckForPromiseVoid(returnAnn);
180
181 if (!hasReturnAnn) {
182 if (!function->HasReturnStatement()) {
183 const auto &loc = genTypeLocation(function);
184 function->SetReturnTypeAnnotation(CreatePromiseVoidType(checker, loc));
185 }
186
187 if (function->HasBody()) {
188 HandleAsyncScriptFunctionBody(checker, function->Body()->AsBlockStatement());
189 }
190 } else if (hasPromiseVoid && function->HasBody()) {
191 HandleAsyncScriptFunctionBody(checker, function->Body()->AsBlockStatement());
192 }
193
194 return ast;
195 };
196
197 program->Ast()->TransformChildrenRecursively(transformer, Name());
198
199 return true;
200 }
201
Postcondition(public_lib::Context * ctx,const parser::Program * program)202 bool PromiseVoidInferencePhase::Postcondition(public_lib::Context *ctx, const parser::Program *program)
203 {
204 (void)ctx;
205
206 auto checkFunctionBody = [](const ir::BlockStatement *body) -> bool {
207 if (!body->IsReturnStatement()) {
208 return true;
209 }
210 auto *returnStmt = body->AsReturnStatement();
211 const auto *arg = returnStmt->Argument();
212
213 if (!arg->IsIdentifier()) {
214 return false;
215 }
216
217 const auto *id = arg->AsIdentifier();
218 return id->Name() == compiler::Signatures::UNDEFINED;
219 };
220
221 auto isOk = true;
222 auto transformer = [checkFunctionBody, &isOk](ir::AstNode *ast) {
223 if (!(ast->IsScriptFunction() && ast->AsScriptFunction()->IsAsyncFunc())) {
224 return;
225 }
226 auto *function = ast->AsScriptFunction();
227 auto *returnAnn = function->ReturnTypeAnnotation();
228 if (!CheckForPromiseVoid(returnAnn)) {
229 return;
230 }
231 if (function->HasBody()) {
232 if (!checkFunctionBody(function->Body()->AsBlockStatement())) {
233 isOk = false;
234 return;
235 }
236 }
237 };
238 program->Ast()->IterateRecursively(transformer);
239
240 return isOk;
241 }
242 } // namespace ark::es2panda::compiler
243