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 &currentName = 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