1 /**
2 * Copyright (c) 2021 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 "tsEnumDeclaration.h"
17
18 #include <ir/astDump.h>
19 #include <ir/expressions/identifier.h>
20 #include <ir/expressions/memberExpression.h>
21 #include <ir/expressions/unaryExpression.h>
22 #include <ir/expressions/binaryExpression.h>
23 #include <ir/expressions/templateLiteral.h>
24 #include <ir/expressions/literals/stringLiteral.h>
25 #include <ir/expressions/literals/numberLiteral.h>
26 #include <ir/ts/tsEnumMember.h>
27 #include <typescript/checker.h>
28
29 namespace panda::es2panda::ir {
30
Iterate(const NodeTraverser & cb) const31 void TSEnumDeclaration::Iterate(const NodeTraverser &cb) const
32 {
33 cb(key_);
34
35 for (auto *it : members_) {
36 cb(it);
37 }
38 }
39
Dump(ir::AstDumper * dumper) const40 void TSEnumDeclaration::Dump(ir::AstDumper *dumper) const
41 {
42 dumper->Add({{"type", "TSEnumDeclaration"}, {"id", key_}, {"members", members_}, {"const", isConst_}});
43 }
44
Compile(compiler::PandaGen * pg) const45 void TSEnumDeclaration::Compile([[maybe_unused]] compiler::PandaGen *pg) const {}
46
ToInt(double num)47 int32_t ToInt(double num)
48 {
49 if (num >= std::numeric_limits<int32_t>::min() && num <= std::numeric_limits<int32_t>::max()) {
50 return static_cast<int32_t>(num);
51 }
52
53 // TODO(aszilagyi): Perform ECMA defined toInt conversion
54
55 return 0;
56 }
57
ToUInt(double num)58 uint32_t ToUInt(double num)
59 {
60 if (num >= std::numeric_limits<uint32_t>::min() && num <= std::numeric_limits<uint32_t>::max()) {
61 return static_cast<int32_t>(num);
62 }
63
64 // TODO(aszilagyi): Perform ECMA defined toInt conversion
65
66 return 0;
67 }
68
EvaluateIdentifier(checker::Checker * checker,binder::EnumVariable * enumVar,const ir::Identifier * expr)69 binder::EnumMemberResult EvaluateIdentifier(checker::Checker *checker, binder::EnumVariable *enumVar,
70 const ir::Identifier *expr)
71 {
72 if (expr->Name() == "NaN") {
73 return std::nan("");
74 }
75 if (expr->Name() == "Infinity") {
76 return std::numeric_limits<double>::infinity();
77 }
78
79 binder::Variable *enumMember = expr->AsIdentifier()->Variable();
80
81 if (!enumMember) {
82 checker->ThrowTypeError({"Cannot find name ", expr->AsIdentifier()->Name()},
83 enumVar->Declaration()->Node()->Start());
84 }
85
86 if (enumMember->IsEnumVariable()) {
87 binder::EnumVariable *exprEnumVar = enumMember->AsEnumVariable();
88 if (std::holds_alternative<bool>(exprEnumVar->Value())) {
89 checker->ThrowTypeError(
90 "A member initializer in a enum declaration cannot reference members declared after it, "
91 "including "
92 "members defined in other enums.",
93 enumVar->Declaration()->Node()->Start());
94 }
95
96 return exprEnumVar->Value();
97 }
98
99 return false;
100 }
101
EvaluateUnaryExpression(checker::Checker * checker,binder::EnumVariable * enumVar,const ir::UnaryExpression * expr)102 binder::EnumMemberResult EvaluateUnaryExpression(checker::Checker *checker, binder::EnumVariable *enumVar,
103 const ir::UnaryExpression *expr)
104 {
105 binder::EnumMemberResult value = TSEnumDeclaration::EvaluateEnumMember(checker, enumVar, expr->Argument());
106 if (!std::holds_alternative<double>(value)) {
107 return false;
108 }
109
110 switch (expr->OperatorType()) {
111 case lexer::TokenType::PUNCTUATOR_PLUS: {
112 return std::get<double>(value);
113 }
114 case lexer::TokenType::PUNCTUATOR_MINUS: {
115 return -std::get<double>(value);
116 }
117 case lexer::TokenType::PUNCTUATOR_TILDE: {
118 return static_cast<double>(~ToInt(std::get<double>(value)));
119 }
120 default: {
121 break;
122 }
123 }
124
125 return false;
126 }
127
EvaluateMemberExpression(checker::Checker * checker,binder::EnumVariable * enumVar,const ir::MemberExpression * expr)128 binder::EnumMemberResult EvaluateMemberExpression(checker::Checker *checker,
129 [[maybe_unused]] binder::EnumVariable *enumVar,
130 const ir::MemberExpression *expr)
131 {
132 if (checker::Checker::IsConstantMemberAccess(expr->AsExpression())) {
133 if (expr->Check(checker)->TypeFlags() == checker::TypeFlag::ENUM) {
134 util::StringView name;
135 if (!expr->IsComputed()) {
136 name = expr->Property()->AsIdentifier()->Name();
137 } else {
138 ASSERT(checker::Checker::IsStringLike(expr->Property()));
139 name = reinterpret_cast<const ir::StringLiteral *>(expr->Property())->Str();
140 }
141
142 // TODO(aszilagyi)
143 }
144 }
145
146 return false;
147 }
148
EvaluateBinaryExpression(checker::Checker * checker,binder::EnumVariable * enumVar,const ir::BinaryExpression * expr)149 binder::EnumMemberResult EvaluateBinaryExpression(checker::Checker *checker, binder::EnumVariable *enumVar,
150 const ir::BinaryExpression *expr)
151 {
152 binder::EnumMemberResult left =
153 TSEnumDeclaration::EvaluateEnumMember(checker, enumVar, expr->AsBinaryExpression()->Left());
154 binder::EnumMemberResult right =
155 TSEnumDeclaration::EvaluateEnumMember(checker, enumVar, expr->AsBinaryExpression()->Right());
156 if (std::holds_alternative<double>(left) && std::holds_alternative<double>(right)) {
157 switch (expr->AsBinaryExpression()->OperatorType()) {
158 case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
159 return static_cast<double>(ToUInt(std::get<double>(left)) | ToUInt(std::get<double>(right)));
160 }
161 case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
162 return static_cast<double>(ToUInt(std::get<double>(left)) & ToUInt(std::get<double>(right)));
163 }
164 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
165 return static_cast<double>(ToUInt(std::get<double>(left)) ^ ToUInt(std::get<double>(right)));
166 }
167 case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: {
168 return static_cast<double>(ToInt(std::get<double>(left)) << ToUInt(std::get<double>(right)));
169 }
170 case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: {
171 return static_cast<double>(ToInt(std::get<double>(left)) >> ToUInt(std::get<double>(right)));
172 }
173 case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: {
174 return static_cast<double>(ToUInt(std::get<double>(left)) >> ToUInt(std::get<double>(right)));
175 }
176 case lexer::TokenType::PUNCTUATOR_PLUS: {
177 return std::get<double>(left) + std::get<double>(right);
178 }
179 case lexer::TokenType::PUNCTUATOR_MINUS: {
180 return std::get<double>(left) - std::get<double>(right);
181 }
182 case lexer::TokenType::PUNCTUATOR_MULTIPLY: {
183 return std::get<double>(left) * std::get<double>(right);
184 }
185 case lexer::TokenType::PUNCTUATOR_DIVIDE: {
186 return std::get<double>(left) / std::get<double>(right);
187 }
188 case lexer::TokenType::PUNCTUATOR_MOD: {
189 return std::fmod(std::get<double>(left), std::get<double>(right));
190 }
191 case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: {
192 return std::pow(std::get<double>(left), std::get<double>(right));
193 }
194 default: {
195 break;
196 }
197 }
198
199 return false;
200 }
201
202 if (std::holds_alternative<util::StringView>(left) && std::holds_alternative<util::StringView>(right) &&
203 expr->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) {
204 std::stringstream ss;
205 ss << std::get<util::StringView>(left) << std::get<util::StringView>(right);
206
207 util::UString res(ss.str(), checker->Allocator());
208 return res.View();
209 }
210
211 return false;
212 }
213
EvaluateEnumMember(checker::Checker * checker,binder::EnumVariable * enumVar,const ir::AstNode * expr)214 binder::EnumMemberResult TSEnumDeclaration::EvaluateEnumMember(checker::Checker *checker, binder::EnumVariable *enumVar,
215 const ir::AstNode *expr)
216 {
217 switch (expr->Type()) {
218 case ir::AstNodeType::UNARY_EXPRESSION: {
219 return EvaluateUnaryExpression(checker, enumVar, expr->AsUnaryExpression());
220 }
221 case ir::AstNodeType::BINARY_EXPRESSION: {
222 return EvaluateBinaryExpression(checker, enumVar, expr->AsBinaryExpression());
223 }
224 case ir::AstNodeType::NUMBER_LITERAL: {
225 return expr->AsNumberLiteral()->Number();
226 }
227 case ir::AstNodeType::STRING_LITERAL: {
228 return expr->AsStringLiteral()->Str();
229 }
230 case ir::AstNodeType::IDENTIFIER: {
231 return EvaluateIdentifier(checker, enumVar, expr->AsIdentifier());
232 }
233 case ir::AstNodeType::MEMBER_EXPRESSION: {
234 return EvaluateEnumMember(checker, enumVar, expr->AsMemberExpression());
235 }
236 default:
237 break;
238 }
239
240 return false;
241 }
242
IsComputedEnumMember(const ir::Expression * init)243 bool IsComputedEnumMember(const ir::Expression *init)
244 {
245 if (init->IsLiteral()) {
246 return !init->AsLiteral()->IsStringLiteral() && !init->AsLiteral()->IsNumberLiteral();
247 }
248
249 if (init->IsTemplateLiteral()) {
250 return !init->AsTemplateLiteral()->Quasis().empty();
251 }
252
253 return true;
254 }
255
AddEnumValueDeclaration(checker::Checker * checker,double number,binder::EnumVariable * variable)256 void AddEnumValueDeclaration(checker::Checker *checker, double number, binder::EnumVariable *variable)
257 {
258 variable->SetTsType(checker->GlobalNumberType());
259
260 util::StringView memberStr = util::Helpers::ToStringView(checker->Allocator(), number);
261
262 binder::TSEnumScope *enumScope = checker->Scope()->AsTSEnumScope();
263 binder::Variable *res = enumScope->FindEnumMemberVariable(memberStr);
264 binder::EnumVariable *enumVar = nullptr;
265
266 if (!res) {
267 auto *decl = checker->Allocator()->New<binder::EnumDecl>(memberStr);
268 CHECK_NOT_NULL(decl);
269 decl->BindNode(variable->Declaration()->Node());
270 enumScope->AddDecl(checker->Allocator(), decl, ScriptExtension::TS);
271 res = enumScope->FindEnumMemberVariable(memberStr);
272 CHECK_NOT_NULL(res);
273 ASSERT(res->IsEnumVariable());
274 enumVar = res->AsEnumVariable();
275 enumVar->AsEnumVariable()->SetBackReference();
276 enumVar->SetTsType(checker->GlobalStringType());
277 } else {
278 ASSERT(res->IsEnumVariable());
279 enumVar = res->AsEnumVariable();
280 auto *decl = checker->Allocator()->New<binder::EnumDecl>(memberStr);
281 CHECK_NOT_NULL(decl);
282 decl->BindNode(variable->Declaration()->Node());
283 enumVar->ResetDecl(decl);
284 }
285
286 enumVar->SetValue(variable->Declaration()->Name());
287 }
288
InferEnumVariableType(checker::Checker * checker,binder::EnumVariable * variable,double * value,bool * initNext,bool * isLiteralEnum,bool isConstEnum,const ir::Expression * computedExpr)289 void InferEnumVariableType(checker::Checker *checker, binder::EnumVariable *variable, double *value, bool *initNext,
290 bool *isLiteralEnum, bool isConstEnum, const ir::Expression *computedExpr)
291 {
292 const ir::Expression *init = variable->Declaration()->Node()->AsTSEnumMember()->Init();
293
294 if (!init) {
295 if (*initNext) {
296 checker->ThrowTypeError("Enum member must have initializer.", variable->Declaration()->Node()->Start());
297 } else {
298 variable->SetValue(++(*value));
299 AddEnumValueDeclaration(checker, *value, variable);
300 return;
301 }
302 }
303
304 if (IsComputedEnumMember(init)) {
305 if (*isLiteralEnum) {
306 checker->ThrowTypeError("Computed values are not permitted in an enum with string valued members.",
307 init->Start());
308 }
309
310 computedExpr = init;
311 }
312
313 binder::EnumMemberResult res = TSEnumDeclaration::EvaluateEnumMember(checker, variable, init);
314 if (std::holds_alternative<util::StringView>(res)) {
315 if (computedExpr) {
316 checker->ThrowTypeError("Computed values are not permitted in an enum with string valued members.",
317 computedExpr->Start());
318 }
319
320 *isLiteralEnum = true;
321 variable->SetTsType(checker->GlobalStringType());
322 *initNext = true;
323 return;
324 }
325
326 if (std::holds_alternative<bool>(res)) {
327 if (isConstEnum) {
328 checker->ThrowTypeError(
329 "const enum member initializers can only contain literal values and other computed enum "
330 "values.",
331 init->Start());
332 }
333
334 *initNext = true;
335 return;
336 }
337
338 ASSERT(std::holds_alternative<double>(res));
339 variable->SetValue(res);
340
341 *value = std::get<double>(res);
342 if (isConstEnum) {
343 if (std::isnan(*value)) {
344 checker->ThrowTypeError("'const' enum member initializer was evaluated to disallowed value 'NaN'.",
345 init->Start());
346 }
347
348 if (std::isinf(*value)) {
349 checker->ThrowTypeError("'const' enum member initializer was evaluated to a non-finite value.",
350 init->Start());
351 }
352 }
353
354 *initNext = false;
355 AddEnumValueDeclaration(checker, *value, variable);
356 }
357
InferType(checker::Checker * checker,bool isConst) const358 checker::Type *TSEnumDeclaration::InferType(checker::Checker *checker, bool isConst) const
359 {
360 double value = -1.0;
361
362 binder::TSEnumScope *enumScope = checker->Scope()->AsTSEnumScope();
363
364 bool initNext = false;
365 bool isLiteralEnum = false;
366 const ir::Expression *computedExpr = nullptr;
367 size_t localsSize = enumScope->Decls().size();
368
369 for (size_t i = 0; i < localsSize; i++) {
370 const util::StringView ¤tName = enumScope->Decls()[i]->Name();
371 binder::Variable *currentVar = enumScope->FindEnumMemberVariable(currentName);
372 CHECK_NOT_NULL(currentVar);
373 ASSERT(currentVar->IsEnumVariable());
374 InferEnumVariableType(checker, currentVar->AsEnumVariable(), &value, &initNext, &isLiteralEnum, isConst,
375 computedExpr);
376 }
377
378 checker::Type *enumType = checker->Allocator()->New<checker::EnumLiteralType>(
379 key_->Name(), checker->Scope(),
380 isLiteralEnum ? checker::EnumLiteralType::EnumLiteralTypeKind::LITERAL
381 : checker::EnumLiteralType::EnumLiteralTypeKind::NUMERIC);
382
383 return enumType;
384 }
385
Check(checker::Checker * checker) const386 checker::Type *TSEnumDeclaration::Check(checker::Checker *checker) const
387 {
388 binder::Variable *enumVar = key_->Variable();
389 // TODO: enumLiteral Identifier binds enumLiteral Variable.
390 if (enumVar == nullptr) {
391 return nullptr;
392 }
393
394 if (!enumVar->TsType()) {
395 checker::ScopeContext scopeCtx(checker, scope_);
396 checker::Type *enumType = InferType(checker, isConst_);
397 CHECK_NOT_NULL(enumType);
398 enumType->SetVariable(enumVar);
399 enumVar->SetTsType(enumType);
400 }
401
402 return nullptr;
403 }
404
UpdateSelf(const NodeUpdater & cb,binder::Binder * binder)405 void TSEnumDeclaration::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
406 {
407 key_ = std::get<ir::AstNode *>(cb(key_))->AsIdentifier();
408
409 for (auto iter = members_.begin(); iter != members_.end(); iter++) {
410 *iter = std::get<ir::AstNode *>(cb(*iter))->AsTSEnumMember();
411 }
412 }
413
414 } // namespace panda::es2panda::ir
415