1/** 2 * Copyright (c) 2021-2024 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 "declgenEts2Ts.h" 17 18#include "ir/base/classProperty.h" 19#include "ir/base/methodDefinition.h" 20#include "ir/base/scriptFunction.h" 21#include "ir/ets/etsImportDeclaration.h" 22#include "ir/expressions/identifier.h" 23#include "ir/expressions/literals/numberLiteral.h" 24#include "ir/module/importSpecifier.h" 25#include "ir/statements/blockStatement.h" 26#include "ir/statements/classDeclaration.h" 27#include "ir/ts/tsEnumMember.h" 28#include "ir/ts/tsInterfaceBody.h" 29#include "ir/ts/tsTypeAliasDeclaration.h" 30#include "ir/ts/tsTypeParameter.h" 31 32#define DEBUG_PRINT 0 33 34namespace ark::es2panda::declgen_ets2ts { 35 36static void DebugPrint([[maybe_unused]] const std::string &msg) 37{ 38#if DEBUG_PRINT 39 std::cerr << msg << std::endl; 40#endif 41} 42 43static void Warning(const std::string &msg) 44{ 45 std::cerr << "Warning declgen ets2ts: " << msg << std::endl; 46} 47 48void TSDeclGen::Generate() 49{ 50 std::stringstream license; 51 license << "/*\n"; 52 license << " * Copyright (c) 2023-2024 Huawei Device Co., Ltd.\n"; 53 license << " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"; 54 license << " * you may not use this file except in compliance with the License.\n"; 55 license << " * You may obtain a copy of the License at\n"; 56 license << " *\n"; 57 license << " * http://www.apache.org/licenses/LICENSE-2.0\n"; 58 license << " *\n"; 59 license << " * Unless required by applicable law or agreed to in writing, software\n"; 60 license << " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"; 61 license << " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"; 62 license << " * See the License for the specific language governing permissions and\n"; 63 license << " * limitations under the License.\n"; 64 license << " */\n\n"; 65 Out(license.str()); 66 Out("declare const exports: any;"); 67 OutEndl(); 68 Out("let ETSGLOBAL: any = (globalThis as any).Panda.getClass('LETSGLOBAL;');"); 69 OutEndl(2U); 70 71 for (auto *globalStatement : program_->Ast()->Statements()) { 72 ResetState(); 73 if (globalStatement->IsETSImportDeclaration()) { 74 GenImportDeclaration(globalStatement->AsETSImportDeclaration()); 75 } else if (globalStatement->IsTSEnumDeclaration()) { 76 GenEnumDeclaration(globalStatement->AsTSEnumDeclaration()); 77 } else if (globalStatement->IsClassDeclaration()) { 78 // The classes generated for enums starts with '#' but those are invalid names and 79 // not requred for the ts code 80 if (globalStatement->AsClassDeclaration()->Definition()->Ident()->Name().Mutf8().find('#') == 81 std::string::npos) { 82 GenClassDeclaration(globalStatement->AsClassDeclaration()); 83 } 84 } else if (globalStatement->IsTSInterfaceDeclaration()) { 85 GenInterfaceDeclaration(globalStatement->AsTSInterfaceDeclaration()); 86 } else if (globalStatement->IsTSTypeAliasDeclaration()) { 87 GenTypeAliasDeclaration(globalStatement->AsTSTypeAliasDeclaration()); 88 } 89 } 90} 91 92template <class T, class CB> 93void TSDeclGen::GenSeparated(const T &container, const CB &cb, const char *separator) 94{ 95 if (container.empty()) { 96 return; 97 } 98 99 cb(container[0]); 100 for (std::size_t i = 1; i < container.size(); ++i) { 101 Out(separator); 102 cb(container[i]); 103 } 104} 105 106void TSDeclGen::ThrowError(const std::string_view message, const lexer::SourcePosition &pos = lexer::SourcePosition()) 107{ 108 lexer::LineIndex index(program_->SourceCode()); 109 const lexer::SourceLocation loc = index.GetLocation(pos); 110 111 throw Error {ErrorType::GENERIC, program_->SourceFilePath().Utf8(), "declgen ets2ts: " + std::string(message), 112 loc.line, loc.col}; 113} 114 115const ir::Identifier *TSDeclGen::GetKeyIdent(const ir::Expression *key) 116{ 117 if (!key->IsIdentifier()) { 118 ThrowError("Not identifier keys are not supported", key->Start()); 119 } 120 121 return key->AsIdentifier(); 122} 123 124static char const *GetDebugTypeName(const checker::Type *checkerType) 125{ 126// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 127#define TYPE_CHECKS(type_flag, typeName) \ 128 if (checkerType->Is##typeName()) { \ 129 return #typeName; \ 130 } 131 TYPE_MAPPING(TYPE_CHECKS) 132#undef TYPE_CHECKS 133 return "unknown type"; 134} 135 136void TSDeclGen::GenType(const checker::Type *checkerType) 137{ 138 DebugPrint(" GenType: "); 139#if DEBUG_PRINT 140 const auto var_name = checkerType->Variable() == nullptr ? "" : checkerType->Variable()->Name().Mutf8(); 141 DebugPrint(std::string(" Converting type: ") + GetDebugTypeName(checkerType) + " (" + var_name + ")"); 142#endif 143 144 if (checkerType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { 145 Out("number"); 146 return; 147 } 148 if (checkerType->HasTypeFlag(checker::TypeFlag::FUNCTION)) { 149 GenFunctionType(checkerType->AsETSFunctionType()); 150 return; 151 } 152 153 switch (checker::ETSChecker::ETSType(checkerType)) { 154 case checker::TypeFlag::ETS_VOID: 155 case checker::TypeFlag::ETS_NULL: 156 case checker::TypeFlag::ETS_UNDEFINED: 157 case checker::TypeFlag::ETS_BOOLEAN: 158 case checker::TypeFlag::ETS_TYPE_PARAMETER: 159 case checker::TypeFlag::ETS_NONNULLISH: 160 case checker::TypeFlag::ETS_READONLY: 161 case checker::TypeFlag::ETS_INT_ENUM: 162 Out(checkerType->ToString()); 163 return; 164 case checker::TypeFlag::ETS_OBJECT: 165 case checker::TypeFlag::ETS_DYNAMIC_TYPE: 166 GenObjectType(checkerType->AsETSObjectType()); 167 return; 168 case checker::TypeFlag::ETS_ARRAY: 169 GenType(checkerType->AsETSArrayType()->ElementType()); 170 Out("[]"); 171 return; 172 case checker::TypeFlag::ETS_UNION: 173 GenUnionType(checkerType->AsETSUnionType()); 174 return; 175 default: 176 ThrowError(std::string("Unsupported type: '") + GetDebugTypeName(checkerType)); 177 } 178} 179 180void TSDeclGen::GenLiteral(const ir::Literal *literal) 181{ 182 if (!literal->IsNumberLiteral()) { 183 ThrowError("Unsupported literal type", literal->Start()); 184 } 185 186 const auto number = literal->AsNumberLiteral()->Number(); 187 if (number.IsInt()) { 188 Out(std::to_string(number.GetInt())); 189 return; 190 } 191 if (number.IsLong()) { 192 Out(std::to_string(number.GetLong())); 193 return; 194 } 195 if (number.IsFloat()) { 196 Out(std::to_string(number.GetFloat())); 197 return; 198 } 199 if (number.IsDouble()) { 200 Out(std::to_string(number.GetDouble())); 201 return; 202 } 203 204 ThrowError("Unexpected number literal type", literal->Start()); 205} 206 207void TSDeclGen::GenFunctionBody(const ir::MethodDefinition *methodDef, const checker::Signature *sig, 208 const bool isConstructor, const bool isSetter) 209{ 210 if (isConstructor) { 211 if (state_.super != nullptr) { 212 Out("{ super(...{} as (ConstructorParameters<typeof "); 213 GenType(state_.super->TsType()); 214 Out(">)); }"); 215 } else { 216 Out(" {}"); 217 } 218 } else if (isSetter) { 219 Out(" {}"); 220 } else { 221 Out(methodDef != nullptr ? ": " : " => "); 222 GenType(sig->ReturnType()); 223 if (methodDef != nullptr && !state_.inInterface) { 224 Out(" { return {} as any; }"); 225 } 226 } 227} 228 229void TSDeclGen::GenFunctionType(const checker::ETSFunctionType *etsFunctionType, const ir::MethodDefinition *methodDef) 230{ 231 const bool isConstructor = methodDef != nullptr ? methodDef->IsConstructor() : false; 232 const bool isSetter = methodDef != nullptr ? methodDef->Kind() == ir::MethodDefinitionKind::SET : false; 233 234 const auto *sig = [this, methodDef, etsFunctionType]() -> const checker::Signature * { 235 if (methodDef != nullptr) { 236 return methodDef->Function()->Signature(); 237 } 238 if (etsFunctionType->CallSignatures().size() != 1) { 239 const auto loc = methodDef != nullptr ? methodDef->Start() : lexer::SourcePosition(); 240 ThrowError("Method overloads are not supported", loc); 241 } 242 return etsFunctionType->CallSignatures()[0]; 243 }(); 244 245 const auto *func = sig->Function(); 246 GenTypeParameters(func->TypeParams()); 247 Out("("); 248 249 GenSeparated(sig->Params(), [this](varbinder::LocalVariable *param) { 250 Out(param->Name()); 251 const auto *paramType = param->TsType(); 252 253 if (param->HasFlag(varbinder::VariableFlags::OPTIONAL)) { 254 Out("?"); 255 } 256 Out(": "); 257 GenType(paramType); 258 }); 259 260 const auto *sigInfo = sig->GetSignatureInfo(); 261 if (sigInfo->restVar != nullptr) { 262 if (!sig->Params().empty()) { 263 Out(", "); 264 } 265 Out("...", sigInfo->restVar->Name().Mutf8(), ": "); 266 GenType(sigInfo->restVar->TsType()); 267 } 268 269 Out(")"); 270 271 GenFunctionBody(methodDef, sig, isConstructor, isSetter); 272} 273 274void TSDeclGen::GenEnumType(const checker::ETSIntEnumType *enumType) 275{ 276 for (auto *member : enumType->GetMembers()) { 277 Out(INDENT); 278 if (!member->IsTSEnumMember()) { 279 ThrowError("Member of enum not of type TSEnumMember", member->Start()); 280 } 281 282 const auto *enumMember = member->AsTSEnumMember(); 283 Out(GetKeyIdent(enumMember->Key())->Name().Mutf8()); 284 const auto *init = enumMember->Init(); 285 if (init != nullptr) { 286 Out(" = "); 287 288 if (!init->IsLiteral()) { 289 ThrowError("Only literal enum initializers are supported", member->Start()); 290 } 291 292 GenLiteral(init->AsLiteral()); 293 } 294 295 Out(","); 296 OutEndl(); 297 } 298} 299 300void TSDeclGen::GenUnionType(const checker::ETSUnionType *unionType) 301{ 302 GenSeparated( 303 unionType->ConstituentTypes(), [this](checker::Type *arg) { GenType(arg); }, " | "); 304} 305 306void TSDeclGen::GenObjectType(const checker::ETSObjectType *objectType) 307{ 308 if (objectType->IsETSStringType()) { 309 Out("string"); 310 return; 311 } 312 if (objectType->HasObjectFlag(checker::ETSObjectFlags::UNBOXABLE_TYPE)) { 313 Out("number"); // NOTE(ivagin): create precise builtin type 314 return; 315 } 316 if (objectType->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL)) { 317 const auto *invoke = objectType->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>( 318 checker::FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME); 319 ASSERT(invoke && invoke->TsType() && invoke->TsType()->IsETSFunctionType()); 320 GenType(invoke->TsType()); 321 return; 322 } 323 if (objectType->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { 324 Out("any"); 325 return; 326 } 327 328 auto typeName = objectType->Name(); 329 if (typeName.Empty()) { 330 Warning("Object type name is empty"); 331 Out("any"); 332 } else { 333 Out(typeName); 334 } 335 336 const auto &typeArgs = objectType->TypeArguments(); 337 if (!typeArgs.empty()) { 338 Out("<"); 339 GenSeparated(typeArgs, [this](checker::Type *arg) { GenType(arg); }); 340 Out(">"); 341 } 342} 343 344void TSDeclGen::GenTypeParameters(const ir::TSTypeParameterDeclaration *typeParams) 345{ 346 if (typeParams != nullptr) { 347 Out("<"); 348 GenSeparated(typeParams->Params(), [this](ir::TSTypeParameter *param) { 349 Out(param->Name()->Name()); 350 auto *constraint = param->Constraint(); 351 if (constraint != nullptr) { 352 Out(" extends "); 353 GenType(constraint->GetType(checker_)); 354 } 355 }); 356 Out(">"); 357 } 358} 359 360void TSDeclGen::GenExport(const ir::Identifier *symbol) 361{ 362 const auto symbolName = symbol->Name().Mutf8(); 363 Out("export {", symbolName, "};"); 364 OutEndl(); 365 if (!symbol->Parent()->IsTSTypeAliasDeclaration() && !symbol->Parent()->IsTSInterfaceDeclaration()) { 366 Out("exports.", symbolName, " = ", symbolName, ";"); 367 } 368 OutEndl(); 369} 370 371void TSDeclGen::GenExport(const ir::Identifier *symbol, const std::string &alias) 372{ 373 const auto symbolName = symbol->Name().Mutf8(); 374 Out("export {", symbolName, " as ", alias, "};"); 375 OutEndl(); 376 if (!symbol->Parent()->IsTSTypeAliasDeclaration() && !symbol->Parent()->IsTSInterfaceDeclaration()) { 377 Out("exports.", alias, " = ", symbolName, ";"); 378 } 379 OutEndl(); 380} 381 382void TSDeclGen::GenDefaultExport(const ir::Identifier *symbol) 383{ 384 const auto symbolName = symbol->Name().Mutf8(); 385 Out("export default ", symbolName, ";"); 386 OutEndl(); 387 if (!symbol->Parent()->IsTSTypeAliasDeclaration() && !symbol->Parent()->IsTSInterfaceDeclaration()) { 388 Out("exports.default = ", symbolName, ";"); 389 } 390 OutEndl(); 391} 392 393void TSDeclGen::ExportIfNeeded(const ir::Identifier *symbol) 394{ 395 if (symbol->Parent()->IsExported()) { 396 GenExport(symbol); 397 } 398 if (symbol->Parent()->IsExportedType()) { 399 GenExport(symbol); 400 } 401 if (symbol->Parent()->IsDefaultExported()) { 402 GenDefaultExport(symbol); 403 } 404} 405 406template <class T> 407void TSDeclGen::GenModifier(const T *node) 408{ 409 if (state_.inInterface) { 410 return; 411 } 412 413 if (node->IsPrivate()) { 414 Out("private "); 415 } 416 if (node->IsProtected()) { 417 Out("protected "); 418 } 419 if (node->IsPublic()) { 420 Out("public "); 421 } 422 if (node->IsReadonly()) { 423 Out("readonly "); 424 } 425 if (node->IsStatic()) { 426 Out("static "); 427 } 428} 429 430void TSDeclGen::GenImportDeclaration(const ir::ETSImportDeclaration *importDeclaration) 431{ 432 DebugPrint("GenImportDeclaration"); 433 if (importDeclaration->IsPureDynamic()) { 434 return; 435 } 436 437 const auto &specifiers = importDeclaration->Specifiers(); 438 Out("import { "); 439 GenSeparated(specifiers, [this, &importDeclaration](ir::AstNode *specifier) { 440 if (!specifier->IsImportSpecifier()) { 441 ThrowError("Only import specifiers are supported", importDeclaration->Start()); 442 } 443 444 const auto local = specifier->AsImportSpecifier()->Local()->Name(); 445 const auto imported = specifier->AsImportSpecifier()->Imported()->Name(); 446 Out(local); 447 if (local != imported) { 448 ThrowError("Imports with local bindings are not supported", importDeclaration->Start()); 449 } 450 }); 451 452 auto source = importDeclaration->Source()->Str().Mutf8(); 453 Out(" } from \"", source, "\";"); 454 OutEndl(2U); 455} 456 457void TSDeclGen::GenTypeAliasDeclaration(const ir::TSTypeAliasDeclaration *typeAlias) 458{ 459 const auto name = typeAlias->Id()->Name().Mutf8(); 460 DebugPrint("GenTypeAliasDeclaration: " + name); 461 const auto *aliasedType = typeAlias->TypeAnnotation()->GetType(checker_); 462 Out("type ", name, " = "); 463 GenType(aliasedType); 464 Out(";"); 465 OutEndl(); 466 467 ExportIfNeeded(typeAlias->Id()); 468 OutEndl(); 469} 470 471void TSDeclGen::GenEnumDeclaration(const ir::TSEnumDeclaration *enumDecl) 472{ 473 const auto enumIdent = GetKeyIdent(enumDecl->Key()); 474 const auto enumName = enumIdent->Name().Mutf8(); 475 DebugPrint("GenEnumDeclaration: " + enumName); 476 Out("enum ", enumName, " {"); 477 OutEndl(); 478 479 ASSERT(enumDecl->TsType()->IsETSIntEnumType()); 480 GenEnumType(enumDecl->TsType()->AsETSIntEnumType()); 481 482 Out("}"); 483 OutEndl(); 484 485 ExportIfNeeded(enumIdent); 486 OutEndl(); 487} 488 489void TSDeclGen::GenInterfaceDeclaration(const ir::TSInterfaceDeclaration *interfaceDecl) 490{ 491 state_.inInterface = true; 492 const auto interfaceName = interfaceDecl->Id()->Name().Mutf8(); 493 DebugPrint("GenInterfaceDeclaration: " + interfaceName); 494 Out("interface ", interfaceName); 495 496 GenTypeParameters(interfaceDecl->TypeParams()); 497 498 Out(" {"); 499 OutEndl(); 500 501 for (auto *prop : interfaceDecl->Body()->Body()) { 502 if (prop->IsMethodDefinition()) { 503 GenMethodDeclaration(prop->AsMethodDefinition()); 504 for (const auto *methodDef : prop->AsMethodDefinition()->Overloads()) { 505 GenMethodDeclaration(methodDef); 506 } 507 } 508 if (prop->IsClassProperty()) { 509 GenPropDeclaration(prop->AsClassProperty()); 510 } 511 } 512 513 Out("}"); 514 OutEndl(); 515 516 ExportIfNeeded(interfaceDecl->Id()); 517 OutEndl(); 518} 519 520void TSDeclGen::GenClassDeclaration(const ir::ClassDeclaration *classDecl) 521{ 522 const auto *classDef = classDecl->Definition(); 523 std::string classDescriptor = "L" + classDef->InternalName().Mutf8() + ";"; 524 std::replace(classDescriptor.begin(), classDescriptor.end(), '.', '/'); 525 state_.currentClassDescriptor = classDescriptor; 526 const auto className = classDef->Ident()->Name().Mutf8(); 527 state_.inGlobalClass = classDef->IsGlobal(); 528 529 DebugPrint("GenClassDeclaration: " + className); 530 531 if (className == compiler::Signatures::DYNAMIC_MODULE_CLASS || className == compiler::Signatures::JSNEW_CLASS || 532 className == compiler::Signatures::JSCALL_CLASS || (className.find("$partial") != std::string::npos)) { 533 return; 534 } 535 536 if (!state_.inGlobalClass) { 537 Out("class ", className); 538 GenTypeParameters(classDef->TypeParams()); 539 540 const auto *super = classDef->Super(); 541 state_.super = super; 542 if (super != nullptr) { 543 Out(" extends "); 544 GenType(super->TsType()); 545 } 546 547 const auto &interfaces = classDef->TsType()->AsETSObjectType()->Interfaces(); 548 if (!interfaces.empty()) { 549 Out(" implements "); 550 ASSERT(classDef->TsType()->IsETSObjectType()); 551 GenSeparated(interfaces, [this](checker::ETSObjectType *interface) { GenType(interface); }); 552 } 553 554 Out(" {"); 555 OutEndl(); 556 } 557 558 for (const auto *prop : classDef->Body()) { 559 if (prop->IsMethodDefinition()) { 560 GenMethodDeclaration(prop->AsMethodDefinition()); 561 for (const auto *methodDef : prop->AsMethodDefinition()->Overloads()) { 562 GenMethodDeclaration(methodDef); 563 } 564 } else if (prop->IsClassProperty()) { 565 GenPropDeclaration(prop->AsClassProperty()); 566 } 567 } 568 569 if (!state_.inGlobalClass) { 570 Out("};"); 571 OutEndl(); 572 Out("(", className, " as any) = (globalThis as any).Panda.getClass('", state_.currentClassDescriptor, "');"); 573 OutEndl(); 574 ExportIfNeeded(classDef->Ident()); 575 OutEndl(); 576 } 577} 578 579void TSDeclGen::GenMethodDeclaration(const ir::MethodDefinition *methodDef) 580{ 581 const auto methodIdent = GetKeyIdent(methodDef->Key()); 582 const auto methodName = methodIdent->Name().Mutf8(); 583 if (methodName.find('#') != std::string::npos) { 584 return; 585 } 586 587 if (state_.inGlobalClass) { 588 Out("function "); 589 } else { 590 Out(INDENT); 591 GenModifier(methodDef); 592 } 593 594 if (methodDef->Kind() == ir::MethodDefinitionKind::GET) { 595 Out("get "); 596 } 597 if (methodDef->Kind() == ir::MethodDefinitionKind::SET) { 598 Out("set "); 599 } 600 601 DebugPrint(" GenMethodDeclaration: " + methodName); 602 Out(methodName); 603 604 if (methodDef->TsType() == nullptr) { 605 Warning("Untyped method encountered: " + methodName); 606 Out(": any"); 607 } else { 608 GenFunctionType(methodDef->TsType()->AsETSFunctionType(), methodDef); 609 } 610 611 Out(";"); 612 OutEndl(); 613 614 if (state_.inGlobalClass) { 615 Out("(", methodName, " as any) = ETSGLOBAL.", methodName, ";"); 616 OutEndl(); 617 ExportIfNeeded(methodIdent); 618 if (methodName == compiler::Signatures::INIT_METHOD) { 619 Out(methodName, "();"); 620 } 621 OutEndl(2U); 622 } 623} 624 625void TSDeclGen::GenPropDeclaration(const ir::ClassProperty *classProp) 626{ 627 if (state_.inGlobalClass) { 628 GenGlobalVarDeclaration(classProp); 629 return; 630 } 631 632 const auto propName = GetKeyIdent(classProp->Key())->Name().Mutf8(); 633 DebugPrint(" GenPropDeclaration: " + propName); 634 635 Out(INDENT); 636 GenModifier(classProp); 637 Out(propName); 638 639 Out(": "); 640 GenType(classProp->TsType()); 641 if (!state_.inInterface) { 642 Out(" = {} as any"); 643 } 644 Out(";"); 645 OutEndl(); 646} 647 648void TSDeclGen::GenGlobalVarDeclaration(const ir::ClassProperty *globalVar) 649{ 650 if (!globalVar->IsExported() && !globalVar->IsDefaultExported()) { 651 return; 652 } 653 654 const auto symbol = GetKeyIdent(globalVar->Key()); 655 const auto varName = symbol->Name().Mutf8(); 656 DebugPrint("GenGlobalVarDeclaration: " + varName); 657 if (!globalVar->IsConst()) { 658 Warning("Not constant global variables are not supported, variable \"" + varName + "\" was skipped"); 659 return; 660 } 661 662 Out("const ", varName, ": "); 663 GenType(globalVar->TsType()); 664 Out(" = ETSGLOBAL.", varName, ';'); 665 OutEndl(); 666 667 GenExport(symbol); 668 OutEndl(); 669} 670 671bool GenerateTsDeclarations(checker::ETSChecker *checker, const ark::es2panda::parser::Program *program, 672 const std::string &outPath) 673{ 674 TSDeclGen declBuilder(checker, program); 675 declBuilder.Generate(); 676 677 std::ofstream outStream(outPath); 678 if (outStream.fail()) { 679 std::cerr << "Failed to open file: " << outPath << std::endl; 680 return false; 681 } 682 683 outStream << declBuilder.Output().str(); 684 outStream.close(); 685 686 return true; 687} 688} // namespace ark::es2panda::declgen_ets2ts 689