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