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