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 
34 namespace ark::es2panda::declgen_ets2ts {
35 
DebugPrint([[maybe_unused]] const std::string &msg)36 static void DebugPrint([[maybe_unused]] const std::string &msg)
37 {
38 #if DEBUG_PRINT
39     std::cerr << msg << std::endl;
40 #endif
41 }
42 
Warning(const std::string &msg)43 static void Warning(const std::string &msg)
44 {
45     std::cerr << "Warning declgen ets2ts: " << msg << std::endl;
46 }
47 
Generate()48 void 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 
92 template <class T, class CB>
GenSeparated(const T &container, const CB &cb, const char *separator)93 void 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 
ThrowError(const std::string_view message, const lexer::SourcePosition &pos = lexer::SourcePosition())106 void 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 
GetKeyIdent(const ir::Expression *key)115 const 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 
GetDebugTypeName(const checker::Type *checkerType)124 static 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 
GenType(const checker::Type *checkerType)136 void 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 
GenLiteral(const ir::Literal *literal)180 void 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 
GenFunctionBody(const ir::MethodDefinition *methodDef, const checker::Signature *sig, const bool isConstructor, const bool isSetter)207 void 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 
GenFunctionType(const checker::ETSFunctionType *etsFunctionType, const ir::MethodDefinition *methodDef)229 void 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 
GenEnumType(const checker::ETSIntEnumType *enumType)274 void 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 
GenUnionType(const checker::ETSUnionType *unionType)300 void TSDeclGen::GenUnionType(const checker::ETSUnionType *unionType)
301 {
302     GenSeparated(
303         unionType->ConstituentTypes(), [this](checker::Type *arg) { GenType(arg); }, " | ");
304 }
305 
GenObjectType(const checker::ETSObjectType *objectType)306 void 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 
GenTypeParameters(const ir::TSTypeParameterDeclaration *typeParams)344 void 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 
GenExport(const ir::Identifier *symbol)360 void 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 
GenExport(const ir::Identifier *symbol, const std::string &alias)371 void 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 
GenDefaultExport(const ir::Identifier *symbol)382 void 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 
ExportIfNeeded(const ir::Identifier *symbol)393 void 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 
406 template <class T>
GenModifier(const T *node)407 void 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 
GenImportDeclaration(const ir::ETSImportDeclaration *importDeclaration)430 void 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 
GenTypeAliasDeclaration(const ir::TSTypeAliasDeclaration *typeAlias)457 void 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 
GenEnumDeclaration(const ir::TSEnumDeclaration *enumDecl)471 void 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 
GenInterfaceDeclaration(const ir::TSInterfaceDeclaration *interfaceDecl)489 void 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 
GenClassDeclaration(const ir::ClassDeclaration *classDecl)520 void 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 
GenMethodDeclaration(const ir::MethodDefinition *methodDef)579 void 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 
GenPropDeclaration(const ir::ClassProperty *classProp)625 void 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 
GenGlobalVarDeclaration(const ir::ClassProperty *globalVar)648 void 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 
GenerateTsDeclarations(checker::ETSChecker *checker, const ark::es2panda::parser::Program *program, const std::string &outPath)671 bool 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