• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &currentName = 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