• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2023 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/ets/etsPrimitiveType.h"
23 #include "ir/expressions/identifier.h"
24 #include "ir/expressions/literals/numberLiteral.h"
25 #include "ir/module/importSpecifier.h"
26 #include "ir/statements/blockStatement.h"
27 #include "ir/statements/classDeclaration.h"
28 #include "ir/ts/tsClassImplements.h"
29 #include "ir/ts/tsEnumMember.h"
30 #include "ir/ts/tsInterfaceBody.h"
31 #include "ir/ts/tsTypeAliasDeclaration.h"
32 #include "ir/ts/tsTypeParameter.h"
33 
34 #define DEBUG_PRINT 0
35 
36 namespace panda::es2panda::util {
37 
DebugPrint(const std::string & msg)38 static void DebugPrint([[maybe_unused]] const std::string &msg)
39 {
40 #if DEBUG_PRINT
41     std::cerr << msg << std::endl;
42 #endif
43 }
44 
Warning(const std::string & msg)45 static void Warning(const std::string &msg)
46 {
47     std::cerr << "Warning declgen ets2ts: " << msg << std::endl;
48 }
49 
Generate()50 void TSDeclGen::Generate()
51 {
52     std::stringstream license;
53     license << "/*\n";
54     license << " * Copyright (c) 2023 Huawei Device Co., Ltd.\n";
55     license << " * Licensed under the Apache License, Version 2.0 (the \"License\");\n";
56     license << " * you may not use this file except in compliance with the License.\n";
57     license << " * You may obtain a copy of the License at\n";
58     license << " *\n";
59     license << " *     http://www.apache.org/licenses/LICENSE-2.0\n";
60     license << " *\n";
61     license << " * Unless required by applicable law or agreed to in writing, software\n";
62     license << " * distributed under the License is distributed on an \"AS IS\" BASIS,\n";
63     license << " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n";
64     license << " * See the License for the specific language governing permissions and\n";
65     license << " * limitations under the License.\n";
66     license << " */\n\n";
67     Out(license.str());
68     Out("declare const exports: any;");
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             GenClassDeclaration(globalStatement->AsClassDeclaration());
79         } else if (globalStatement->IsTSInterfaceDeclaration()) {
80             GenInterfaceDeclaration(globalStatement->AsTSInterfaceDeclaration());
81         } else if (globalStatement->IsTSTypeAliasDeclaration()) {
82             GenTypeAliasDeclaration(globalStatement->AsTSTypeAliasDeclaration());
83         }
84     }
85 }
86 
87 template <class T, class CB>
GenCommaSeparated(const T & container,const CB & cb)88 void TSDeclGen::GenCommaSeparated(const T &container, const CB &cb)
89 {
90     if (container.empty()) {
91         return;
92     }
93 
94     cb(container[0]);
95     for (std::size_t i = 1; i < container.size(); ++i) {
96         Out(", ");
97         cb(container[i]);
98     }
99 }
100 
ThrowError(const std::string_view message,const lexer::SourcePosition & pos=lexer::SourcePosition ())101 void TSDeclGen::ThrowError(const std::string_view message, const lexer::SourcePosition &pos = lexer::SourcePosition())
102 {
103     lexer::LineIndex index(program_->SourceCode());
104     const lexer::SourceLocation loc = index.GetLocation(pos);
105 
106     throw Error {ErrorType::GENERIC, program_->SourceFilePath().Utf8(), "declgen ets2ts: " + std::string(message),
107                  loc.line, loc.col};
108 }
109 
GetKeyName(const ir::Expression * key)110 std::string TSDeclGen::GetKeyName(const ir::Expression *key)
111 {
112     if (!key->IsIdentifier()) {
113         ThrowError("Not identifier keys are not supported", key->Start());
114     }
115 
116     return key->AsIdentifier()->Name().Mutf8();
117 }
118 
GenType(const checker::Type * checkerType)119 void TSDeclGen::GenType(const checker::Type *checkerType)
120 {
121     // NOTE: vpukhov. rewrite when nullish type is implemented with union
122     GenTypeNonNullish(checkerType);
123     if (checkerType->IsNullish()) {
124         if (checkerType->ContainsNull()) {
125             Out(" | null");
126         }
127         if (checkerType->ContainsUndefined()) {
128             Out(" | undefined");
129         }
130     }
131 }
132 
GenTypeNonNullish(const checker::Type * checkerType)133 void TSDeclGen::GenTypeNonNullish(const checker::Type *checkerType)
134 {
135     ASSERT(checkerType != nullptr);
136     DebugPrint("  GenType: ");
137 #if DEBUG_PRINT
138 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
139 #define TYPE_CHECKS(type_flag, typeName)                                                                         \
140     if (checkerType->Is##typeName()) {                                                                           \
141         const auto var_name = checkerType->Variable() == nullptr ? "" : checkerType->Variable()->Name().Mutf8(); \
142         DebugPrint("  Converting type: " #typeName " (" + var_name + ")");                                       \
143     }
144     TYPE_MAPPING(TYPE_CHECKS)
145 #undef TYPE_CHECKS
146 #endif
147 
148     if (checkerType->IsCharType() || checkerType->IsByteType() || checkerType->IsIntType() ||
149         checkerType->IsShortType() || checkerType->IsNumberType() || checkerType->IsLongType() ||
150         checkerType->IsFloatType() || checkerType->IsDoubleType()) {
151         Out("number");
152     } else if (checkerType->IsETSBooleanType()) {
153         Out("boolean");
154     } else if (checkerType->IsETSVoidType()) {
155         Out("void");
156     } else if (checkerType->IsETSStringType()) {
157         Out("string");
158     } else if (checkerType->IsETSArrayType()) {
159         GenType(checkerType->AsETSArrayType()->ElementType());
160         Out("[]");
161     } else if (checkerType->IsETSEnumType()) {
162         GenEnumType(checkerType->AsETSEnumType());
163     } else if (checkerType->IsETSFunctionType()) {
164         GenFunctionType(checkerType->AsETSFunctionType());
165     } else if (checkerType->IsETSObjectType()) {
166         if (checker_->IsTypeBuiltinType(checkerType)) {
167             Out("number");
168             return;
169         }
170         GenObjectType(checkerType->AsETSObjectType());
171     } else if (checkerType->IsETSTypeParameter()) {
172         GenTypeParameterType(checkerType->AsETSTypeParameter());
173     } else {
174 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
175 #define TYPE_CHECKS(typeFlag, typeName)              \
176     if (checkerType->Is##typeName()) {               \
177         ThrowError("Unsupported type: '" #typeName); \
178     }
179         TYPE_MAPPING(TYPE_CHECKS)
180 #undef TYPE_CHECKS
181         UNREACHABLE();
182     }
183 }
184 
GenLiteral(const ir::Literal * literal)185 void TSDeclGen::GenLiteral(const ir::Literal *literal)
186 {
187     if (!literal->IsNumberLiteral()) {
188         ThrowError("Unsupported literal type", literal->Start());
189     }
190 
191     const auto number = literal->AsNumberLiteral()->Number();
192     if (number.IsInt()) {
193         Out(std::to_string(number.GetInt()));
194         return;
195     }
196     if (number.IsLong()) {
197         Out(std::to_string(number.GetLong()));
198         return;
199     }
200     if (number.IsFloat()) {
201         Out(std::to_string(number.GetFloat()));
202         return;
203     }
204     if (number.IsDouble()) {
205         Out(std::to_string(number.GetDouble()));
206         return;
207     }
208 
209     ThrowError("Unexpected number literal type", literal->Start());
210 }
211 
GenFunctionType(const checker::ETSFunctionType * etsFunctionType,const ir::MethodDefinition * methodDef)212 void TSDeclGen::GenFunctionType(const checker::ETSFunctionType *etsFunctionType, const ir::MethodDefinition *methodDef)
213 {
214     const bool isConstructor = methodDef != nullptr ? methodDef->IsConstructor() : false;
215 
216     if (etsFunctionType->CallSignatures().size() != 1) {
217         const auto loc = methodDef != nullptr ? methodDef->Start() : lexer::SourcePosition();
218         ThrowError("Method overloads are not supported", loc);
219     }
220 
221     for (const auto *sig : etsFunctionType->CallSignatures()) {
222         const auto *func = sig->Function();
223 
224         GenTypeParameters(func->TypeParams());
225 
226         Out("(");
227 
228         GenCommaSeparated(sig->Params(), [this](varbinder::LocalVariable *param) {
229             Out(param->Name());
230             const auto *paramType = param->TsType();
231 
232             if (param->HasFlag(varbinder::VariableFlags::OPTIONAL) || paramType->IsNullish()) {
233                 Out("?");
234             }
235 
236             Out(": ");
237             GenType(paramType);
238         });
239 
240         const auto *sigInfo = sig->GetSignatureInfo();
241         if (sigInfo->restVar != nullptr) {
242             if (!sig->Params().empty()) {
243                 Out(", ");
244             }
245             Out("...", sigInfo->restVar->Name().Mutf8(), ": ");
246             GenType(sigInfo->restVar->TsType());
247         }
248 
249         Out(")");
250 
251         if (isConstructor) {
252             if (state_.super != nullptr) {
253                 Out("{ super(...{} as (ConstructorParameters<typeof ");
254                 GenType(state_.super->TsType());
255                 Out(">)); }");
256             } else {
257                 Out(" {}");
258             }
259         } else {
260             Out(methodDef != nullptr ? ": " : " => ");
261             GenType(sig->ReturnType());
262             if (methodDef != nullptr && !state_.inInterface) {
263                 Out(" { return {} as any; }");
264             }
265         }
266     }
267 }
268 
GenEnumType(const checker::ETSEnumType * enumType)269 void TSDeclGen::GenEnumType(const checker::ETSEnumType *enumType)
270 {
271     for (auto *member : enumType->GetMembers()) {
272         Out(INDENT);
273         if (!member->IsTSEnumMember()) {
274             ThrowError("Member of enum not of type TSEnumMember", member->Start());
275         }
276 
277         const auto *enumMember = member->AsTSEnumMember();
278         Out(GetKeyName(enumMember->Key()));
279         const auto *init = enumMember->Init();
280         if (init != nullptr) {
281             Out(" = ");
282 
283             if (!init->IsLiteral()) {
284                 ThrowError("Only literal enum initializers are supported", member->Start());
285             }
286 
287             GenLiteral(init->AsLiteral());
288         }
289 
290         Out(",");
291         OutEndl();
292     }
293 }
294 
GenObjectType(const checker::ETSObjectType * objectType)295 void TSDeclGen::GenObjectType(const checker::ETSObjectType *objectType)
296 {
297     if (objectType->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL)) {
298         const auto *invoke = objectType->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>("invoke");
299         ASSERT(invoke && invoke->TsType() && invoke->TsType()->IsETSFunctionType());
300         GenType(invoke->TsType());
301         return;
302     }
303 
304     if (objectType->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) {
305         Out("any");
306         return;
307     }
308 
309     auto typeName = objectType->Name();
310     if (typeName.Empty()) {
311         Warning("Object type name is empty");
312         Out("any");
313     } else {
314         Out(typeName);
315     }
316 
317     const auto &typeArgs = objectType->TypeArguments();
318     if (!typeArgs.empty()) {
319         Out("<");
320         GenCommaSeparated(typeArgs, [this](checker::Type *arg) { GenType(arg); });
321         Out(">");
322     }
323 }
324 
GenTypeParameterType(const checker::ETSTypeParameter * typeParam)325 void TSDeclGen::GenTypeParameterType(const checker::ETSTypeParameter *typeParam)
326 {
327     Out(typeParam->GetDeclNode()->Name()->Name());
328 }
329 
GenTypeParameters(const ir::TSTypeParameterDeclaration * typeParams)330 void TSDeclGen::GenTypeParameters(const ir::TSTypeParameterDeclaration *typeParams)
331 {
332     if (typeParams != nullptr) {
333         Out("<");
334         GenCommaSeparated(typeParams->Params(), [this](ir::TSTypeParameter *param) {
335             Out(param->Name()->Name());
336             auto *constraint = param->Constraint();
337             if (constraint != nullptr) {
338                 Out(" extends ");
339                 GenType(constraint->GetType(checker_));
340             }
341         });
342         Out(">");
343     }
344 }
345 
346 template <class T>
GenModifier(const T * node)347 void TSDeclGen::GenModifier(const T *node)
348 {
349     if (state_.inInterface) {
350         return;
351     }
352 
353     if (node->IsPrivate()) {
354         Out("private ");
355     }
356     if (node->IsProtected()) {
357         Out("protected ");
358     }
359     if (node->IsPublic()) {
360         Out("public ");
361     }
362     if (node->IsReadonly()) {
363         Out("readonly ");
364     }
365     if (node->IsStatic()) {
366         Out("static ");
367     }
368 }
369 
GenImportDeclaration(const ir::ETSImportDeclaration * importDeclaration)370 void TSDeclGen::GenImportDeclaration(const ir::ETSImportDeclaration *importDeclaration)
371 {
372     DebugPrint("GenImportDeclaration");
373     if (importDeclaration->IsPureDynamic()) {
374         return;
375     }
376 
377     const auto &specifiers = importDeclaration->Specifiers();
378     Out("import { ");
379     GenCommaSeparated(specifiers, [this, &importDeclaration](ir::AstNode *specifier) {
380         if (!specifier->IsImportSpecifier()) {
381             ThrowError("Only import specifiers are supported", importDeclaration->Start());
382         }
383 
384         const auto local = specifier->AsImportSpecifier()->Local()->Name();
385         const auto imported = specifier->AsImportSpecifier()->Imported()->Name();
386         if (local != imported) {
387             ThrowError("Imports with local bindings are not supported", importDeclaration->Start());
388         }
389 
390         Out(local);
391     });
392 
393     auto source = importDeclaration->Source()->Str().Mutf8();
394     if (importDeclaration->Module() != nullptr) {
395         source += "/" + importDeclaration->Module()->Str().Mutf8();
396     }
397 
398     Out(" } from \"", source, "\";");
399     OutEndl(2U);
400 }
401 
GenTypeAliasDeclaration(const ir::TSTypeAliasDeclaration * typeAlias)402 void TSDeclGen::GenTypeAliasDeclaration(const ir::TSTypeAliasDeclaration *typeAlias)
403 {
404     const auto name = typeAlias->Id()->Name().Mutf8();
405     DebugPrint("GenTypeAliasDeclaration: " + name);
406     const auto *aliasedType = typeAlias->TypeAnnotation()->GetType(checker_);
407     Out("export type ", name, " = ");
408     GenType(aliasedType);
409     Out(";");
410     OutEndl(2U);
411 }
412 
GenEnumDeclaration(const ir::TSEnumDeclaration * enumDecl)413 void TSDeclGen::GenEnumDeclaration(const ir::TSEnumDeclaration *enumDecl)
414 {
415     const auto enumName = GetKeyName(enumDecl->Key());
416     DebugPrint("GenEnumDeclaration: " + enumName);
417     Out("export enum ", enumName, " {");
418     OutEndl();
419 
420     ASSERT(enumDecl->TsType()->IsETSEnumType());
421     GenEnumType(enumDecl->TsType()->AsETSEnumType());
422 
423     Out("}");
424     OutEndl(2U);
425 }
426 
GenInterfaceDeclaration(const ir::TSInterfaceDeclaration * interfaceDecl)427 void TSDeclGen::GenInterfaceDeclaration(const ir::TSInterfaceDeclaration *interfaceDecl)
428 {
429     state_.inInterface = true;
430     const auto interfaceName = interfaceDecl->Id()->Name().Mutf8();
431     DebugPrint("GenInterfaceDeclaration: " + interfaceName);
432     Out("export interface ", interfaceName);
433 
434     GenTypeParameters(interfaceDecl->TypeParams());
435 
436     Out(" {");
437     OutEndl();
438 
439     for (auto *prop : interfaceDecl->Body()->Body()) {
440         if (prop->IsMethodDefinition()) {
441             GenMethodDeclaration(prop->AsMethodDefinition());
442         }
443         if (prop->IsClassProperty()) {
444             GenPropDeclaration(prop->AsClassProperty());
445         }
446     }
447 
448     Out("}");
449     OutEndl(2U);
450 }
451 
GenClassDeclaration(const ir::ClassDeclaration * classDecl)452 void TSDeclGen::GenClassDeclaration(const ir::ClassDeclaration *classDecl)
453 {
454     const auto *classDef = classDecl->Definition();
455     std::string classDescriptor = "L" + classDef->InternalName().Mutf8() + ";";
456     std::replace(classDescriptor.begin(), classDescriptor.end(), '.', '/');
457     state_.currentClassDescriptor = classDescriptor;
458     const auto className = classDef->Ident()->Name().Mutf8();
459     state_.inGlobalClass = classDef->IsGlobal();
460 
461     DebugPrint("GenClassDeclaration: " + className);
462 
463     if (className == compiler::Signatures::DYNAMIC_MODULE_CLASS || className == compiler::Signatures::JSNEW_CLASS ||
464         className == compiler::Signatures::JSCALL_CLASS) {
465         return;
466     }
467 
468     if (!state_.inGlobalClass) {
469         Out("class ", className);
470 
471         GenTypeParameters(classDef->TypeParams());
472 
473         const auto *super = classDef->Super();
474         state_.super = super;
475         if (super != nullptr) {
476             Out(" extends ");
477             GenType(super->TsType());
478         }
479 
480         const auto &interfaces = classDef->TsType()->AsETSObjectType()->Interfaces();
481         if (!interfaces.empty()) {
482             Out(" implements ");
483             ASSERT(classDef->TsType()->IsETSObjectType());
484             GenCommaSeparated(interfaces, [this](checker::ETSObjectType *interface) { GenType(interface); });
485         }
486 
487         Out(" {");
488         OutEndl();
489     }
490 
491     for (const auto *prop : classDef->Body()) {
492         if (prop->IsMethodDefinition()) {
493             GenMethodDeclaration(prop->AsMethodDefinition());
494         } else if (prop->IsClassProperty()) {
495             GenPropDeclaration(prop->AsClassProperty());
496         }
497     }
498 
499     if (!state_.inGlobalClass) {
500         Out("};");
501         OutEndl();
502         Out("(", className, " as any) = (globalThis as any).Panda.getClass('", state_.currentClassDescriptor, "');");
503         OutEndl();
504         Out("export {", className, "};");
505         OutEndl();
506         Out("exports.", className, " = ", className, ";");
507         OutEndl(2U);
508     }
509 }
510 
GenMethodDeclaration(const ir::MethodDefinition * methodDef)511 void TSDeclGen::GenMethodDeclaration(const ir::MethodDefinition *methodDef)
512 {
513     switch (methodDef->Kind()) {
514         case ir::MethodDefinitionKind::GET:
515         case ir::MethodDefinitionKind::SET: {
516             ThrowError("Unsupported method kind", methodDef->Start());
517         }
518         default:
519             break;
520     }
521 
522     if (state_.inGlobalClass) {
523         Out("function ");
524     } else {
525         Out(INDENT);
526         GenModifier(methodDef);
527     }
528 
529     const auto methodName = GetKeyName(methodDef->Key());
530     DebugPrint("  GenMethodDeclaration: " + methodName);
531     Out(methodName);
532 
533     if (methodDef->TsType() == nullptr) {
534         Warning("Untyped method encountered: " + methodName);
535         Out(": any");
536     } else {
537         GenFunctionType(methodDef->TsType()->AsETSFunctionType(), methodDef);
538     }
539 
540     Out(";");
541     OutEndl();
542 
543     if (state_.inGlobalClass) {
544         Out("(", methodName, " as any) = (globalThis as any).Panda.getFunction('", state_.currentClassDescriptor,
545             "', '", methodName, "');");
546         OutEndl();
547         Out("export {", methodName, "};");
548         OutEndl();
549         Out("exports.", methodName, " = ", methodName, ";");
550         OutEndl();
551         if (methodName == compiler::Signatures::INIT_METHOD) {
552             Out(methodName, "();");
553         }
554         OutEndl(2U);
555     }
556 }
557 
GenPropDeclaration(const ir::ClassProperty * classProp)558 void TSDeclGen::GenPropDeclaration(const ir::ClassProperty *classProp)
559 {
560     if (state_.inGlobalClass) {
561         return;
562     }
563 
564     const auto propName = GetKeyName(classProp->Key());
565     DebugPrint("  GenPropDeclaration: " + propName);
566 
567     Out(INDENT);
568     GenModifier(classProp);
569     Out(propName);
570 
571     Out(": ");
572     GenType(classProp->TsType());
573     if (!state_.inInterface) {
574         Out(" = {} as any");
575     }
576     Out(";");
577     OutEndl();
578 }
579 
GenerateTsDeclarations(checker::ETSChecker * checker,const panda::es2panda::parser::Program * program,const std::string & outPath)580 bool GenerateTsDeclarations(checker::ETSChecker *checker, const panda::es2panda::parser::Program *program,
581                             const std::string &outPath)
582 {
583     TSDeclGen declBuilder(checker, program);
584     declBuilder.Generate();
585 
586     std::ofstream outStream(outPath);
587     if (outStream.fail()) {
588         std::cerr << "Failed to open file: " << outPath << std::endl;
589         return false;
590     }
591 
592     outStream << declBuilder.Output().str();
593     outStream.close();
594 
595     return true;
596 }
597 }  // namespace panda::es2panda::util
598