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 31namespace panda::es2panda::ir { 32 33void TSEnumDeclaration::Iterate(const NodeTraverser &cb) const 34{ 35 cb(key_); 36 37 for (auto *it : members_) { 38 cb(it); 39 } 40} 41 42void TSEnumDeclaration::Dump(ir::AstDumper *dumper) const 43{ 44 dumper->Add({{"type", "TSEnumDeclaration"}, {"id", key_}, {"members", members_}, {"const", isConst_}}); 45} 46 47void TSEnumDeclaration::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} 48 49int32_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 60uint32_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 71binder::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 104binder::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 130binder::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 151binder::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 216binder::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 245bool 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 258void 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 291void 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 360checker::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 388checker::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 407void 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