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(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,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 decl->BindNode(variable->Declaration()->Node());
271 enumScope->AddDecl(checker->Allocator(), decl, ScriptExtension::TS);
272 res = enumScope->FindEnumMemberVariable(memberStr);
273 ASSERT(res && 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 decl->BindNode(variable->Declaration()->Node());
282 enumVar->ResetDecl(decl);
283 }
284
285 enumVar->SetValue(variable->Declaration()->Name());
286 }
287
InferEnumVariableType(checker::Checker * checker,binder::EnumVariable * variable,double * value,bool * initNext,bool * isLiteralEnum,bool isConstEnum,const ir::Expression * computedExpr)288 void InferEnumVariableType(checker::Checker *checker, binder::EnumVariable *variable, double *value, bool *initNext,
289 bool *isLiteralEnum, bool isConstEnum, const ir::Expression *computedExpr)
290 {
291 const ir::Expression *init = variable->Declaration()->Node()->AsTSEnumMember()->Init();
292
293 if (!init && *initNext) {
294 checker->ThrowTypeError("Enum member must have initializer.", variable->Declaration()->Node()->Start());
295 }
296
297 if (!init && !*initNext) {
298 variable->SetValue(++(*value));
299 AddEnumValueDeclaration(checker, *value, variable);
300 return;
301 }
302
303 ASSERT(init);
304
305 if (IsComputedEnumMember(init)) {
306 if (*isLiteralEnum) {
307 checker->ThrowTypeError("Computed values are not permitted in an enum with string valued members.",
308 init->Start());
309 }
310
311 computedExpr = init;
312 }
313
314 binder::EnumMemberResult res = TSEnumDeclaration::EvaluateEnumMember(checker, variable, init);
315 if (std::holds_alternative<util::StringView>(res)) {
316 if (computedExpr) {
317 checker->ThrowTypeError("Computed values are not permitted in an enum with string valued members.",
318 computedExpr->Start());
319 }
320
321 *isLiteralEnum = true;
322 variable->SetTsType(checker->GlobalStringType());
323 *initNext = true;
324 return;
325 }
326
327 if (std::holds_alternative<bool>(res)) {
328 if (isConstEnum) {
329 checker->ThrowTypeError(
330 "const enum member initializers can only contain literal values and other computed enum "
331 "values.",
332 init->Start());
333 }
334
335 *initNext = true;
336 return;
337 }
338
339 ASSERT(std::holds_alternative<double>(res));
340 variable->SetValue(res);
341
342 *value = std::get<double>(res);
343 if (isConstEnum) {
344 if (std::isnan(*value)) {
345 checker->ThrowTypeError("'const' enum member initializer was evaluated to disallowed value 'NaN'.",
346 init->Start());
347 }
348
349 if (std::isinf(*value)) {
350 checker->ThrowTypeError("'const' enum member initializer was evaluated to a non-finite value.",
351 init->Start());
352 }
353 }
354
355 *initNext = false;
356 AddEnumValueDeclaration(checker, *value, variable);
357 }
358
InferType(checker::Checker * checker,bool isConst) const359 checker::Type *TSEnumDeclaration::InferType(checker::Checker *checker, bool isConst) const
360 {
361 double value = -1.0;
362
363 binder::TSEnumScope *enumScope = checker->Scope()->AsTSEnumScope();
364
365 bool initNext = false;
366 bool isLiteralEnum = false;
367 const ir::Expression *computedExpr = nullptr;
368 size_t localsSize = enumScope->Decls().size();
369
370 for (size_t i = 0; i < localsSize; i++) {
371 const util::StringView ¤tName = enumScope->Decls()[i]->Name();
372 binder::Variable *currentVar = enumScope->FindEnumMemberVariable(currentName);
373 ASSERT(currentVar && 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 enumType->SetVariable(enumVar);
398 enumVar->SetTsType(enumType);
399 }
400
401 return nullptr;
402 }
403
UpdateSelf(const NodeUpdater & cb,binder::Binder * binder)404 void TSEnumDeclaration::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
405 {
406 key_ = std::get<ir::AstNode *>(cb(key_))->AsIdentifier();
407
408 for (auto iter = members_.begin(); iter != members_.end(); iter++) {
409 *iter = std::get<ir::AstNode *>(cb(*iter))->AsTSEnumMember();
410 }
411 }
412
413 } // namespace panda::es2panda::ir
414