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