• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2025 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 "interfaceObjectLiteralLowering.h"
17 #include "checker/ETSchecker.h"
18 #include "checker/ets/typeRelationContext.h"
19 #include "compiler/lowering/util.h"
20 #include "generated/signatures.h"
21 #include "ir/expressions/assignmentExpression.h"
22 #include "util/helpers.h"
23 
24 namespace ark::es2panda::compiler {
25 
26 static constexpr std::string_view OBJECT_LITERAL_SUFFIX = "$ObjectLiteral";
27 using ReadonlyFieldHolder =
28     std::tuple<util::UString, util::StringView, checker::Type *>;  // anonClassFieldName, paramName, fieldType
29 
Name() const30 std::string_view InterfaceObjectLiteralLowering::Name() const
31 {
32     return "InterfaceObjectLiteralLowering";
33 }
34 
IsInterfaceType(const checker::Type * type)35 static inline bool IsInterfaceType(const checker::Type *type)
36 {
37     return type != nullptr && type->IsETSObjectType() &&
38            type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE) &&
39            !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC);
40 }
41 
IsAbstractClassType(const checker::Type * type)42 static inline bool IsAbstractClassType(const checker::Type *type)
43 {
44     return type != nullptr && type->IsETSObjectType() &&
45            type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT) &&
46            !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC);
47 }
48 
CreateAnonClassImplCtor(public_lib::Context * ctx,ArenaVector<ReadonlyFieldHolder> & readonlyFields)49 static ir::AstNode *CreateAnonClassImplCtor(public_lib::Context *ctx, ArenaVector<ReadonlyFieldHolder> &readonlyFields)
50 {
51     auto *const checker = ctx->checker->AsETSChecker();
52     auto *const parser = ctx->parser->AsETSParser();
53     checker::ETSChecker::ClassInitializerBuilder initBuilder =
54         [ctx, checker, parser, readonlyFields](ArenaVector<ir::Statement *> *statements,
55                                                ArenaVector<ir::Expression *> *params) {
56             for (auto [anonClassFieldName, paramName, retType] : readonlyFields) {
57                 ir::ETSParameterExpression *param =
58                     checker->AddParam(paramName, ctx->AllocNode<ir::OpaqueTypeNode>(retType, ctx->Allocator()));
59                 params->push_back(param);
60                 auto *paramIdent = ctx->AllocNode<ir::Identifier>(paramName, ctx->Allocator());
61                 statements->push_back(
62                     parser->CreateFormattedStatement("this.@@I1 = @@I2;", anonClassFieldName, paramIdent));
63             }
64             checker->AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
65         };
66 
67     return checker->CreateClassInstanceInitializer(initBuilder);
68 }
69 
CreateAnonClassField(public_lib::Context * ctx,ir::MethodDefinition * ifaceMethod,util::UString anonClassFieldName)70 static ir::ClassProperty *CreateAnonClassField(public_lib::Context *ctx, ir::MethodDefinition *ifaceMethod,
71                                                util::UString anonClassFieldName)
72 {
73     auto *const parser = ctx->parser->AsETSParser();
74     // Field type annotation
75     ES2PANDA_ASSERT(ifaceMethod->Function());
76     auto *fieldType = ifaceMethod->Function()->Signature()->ReturnType();
77 
78     std::stringstream sourceCode;
79     // Field modifiers flags
80     sourceCode << "private ";
81     // No overloads means no setter function with the same name, so the field is readonly
82     if (ifaceMethod->Overloads().empty()) {
83         sourceCode << "readonly ";
84     }
85     sourceCode << "@@I1 : @@T2;" << std::endl;
86 
87     auto field = parser->CreateFormattedClassFieldDefinition(sourceCode.str(), anonClassFieldName, fieldType);
88     field->SetRange(ifaceMethod->Range());
89 
90     return field->AsClassProperty();
91 }
92 
CreateAnonClassFieldGetterSetter(public_lib::Context * ctx,ir::MethodDefinition * ifaceMethod,bool isSetter,util::UString anonClassFieldName)93 static ir::MethodDefinition *CreateAnonClassFieldGetterSetter(public_lib::Context *ctx,
94                                                               ir::MethodDefinition *ifaceMethod, bool isSetter,
95                                                               util::UString anonClassFieldName)
96 {
97     auto *const parser = ctx->parser->AsETSParser();
98     // Field type annotation
99     auto *fieldType = ifaceMethod->Function()->Signature()->ReturnType();
100     ES2PANDA_ASSERT(fieldType != nullptr);
101 
102     std::stringstream sourceCode;
103 
104     if (isSetter) {
105         // Setter body: this.<fieldName> = <callParam>;
106         sourceCode << "public set @@I1 (anonParam:@@T2){" << std::endl;
107         sourceCode << "this.@@I3 = anonParam" << std::endl;
108         sourceCode << "}" << std::endl;
109         ES2PANDA_ASSERT(ifaceMethod->Id());
110         return parser
111             ->CreateFormattedClassMethodDefinition(sourceCode.str(), ifaceMethod->Id()->Name(), fieldType,
112                                                    anonClassFieldName)
113             ->AsMethodDefinition();
114     }
115 
116     // Getter body: return this.<fieldName>;
117     sourceCode << "public get @@I1():@@T2{" << std::endl;
118     sourceCode << "return this.@@I3" << std::endl;
119     sourceCode << "}" << std::endl;
120 
121     return parser
122         ->CreateFormattedClassMethodDefinition(sourceCode.str(), ifaceMethod->Id()->Name(), fieldType,
123                                                anonClassFieldName)
124         ->AsMethodDefinition();
125 }
126 
FillClassBody(public_lib::Context * ctx,ArenaVector<ir::AstNode * > * classBody,const ArenaVector<ir::AstNode * > & ifaceBody,ArenaVector<ReadonlyFieldHolder> & readonlyFields,checker::ETSObjectType * currentType=nullptr)127 static void FillClassBody(public_lib::Context *ctx, ArenaVector<ir::AstNode *> *classBody,
128                           const ArenaVector<ir::AstNode *> &ifaceBody, ArenaVector<ReadonlyFieldHolder> &readonlyFields,
129                           checker::ETSObjectType *currentType = nullptr)
130 {
131     for (auto *it : ifaceBody) {
132         ES2PANDA_ASSERT(it->IsMethodDefinition());
133         auto *ifaceMethod = it->AsMethodDefinition();
134 
135         ES2PANDA_ASSERT(ifaceMethod->Function());
136         if (!ifaceMethod->Function()->IsGetter()) {
137             continue;
138         }
139 
140         auto iter = std::find_if(classBody->begin(), classBody->end(), [ifaceMethod](ir::AstNode *ast) -> bool {
141             return ast->IsMethodDefinition() && ast->AsMethodDefinition()->Function()->IsGetter() &&
142                    ast->AsMethodDefinition()->Id()->Name() == ifaceMethod->Id()->Name();
143         });
144         if (iter != classBody->end()) {
145             continue;
146         }
147 
148         auto copyIfaceMethod = ifaceMethod->Clone(ctx->Allocator(), nullptr);
149         copyIfaceMethod->SetRange(ifaceMethod->Range());
150         copyIfaceMethod->Function()->SetSignature(ifaceMethod->Function()->Signature());
151 
152         if (currentType != nullptr) {
153             auto instanProp =
154                 currentType->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>(ifaceMethod->Id()->Name());
155             auto funcType = (instanProp != nullptr) ? instanProp->TsType() : nullptr;
156             if (funcType != nullptr) {
157                 ES2PANDA_ASSERT(funcType->IsETSFunctionType() &&
158                                 funcType->AsETSFunctionType()->FindGetter() != nullptr);
159                 copyIfaceMethod->Function()->SetSignature(funcType->AsETSFunctionType()->FindGetter());
160             }
161         }
162 
163         // Field identifier
164         util::UString anonClassFieldName(
165             std::string(compiler::Signatures::PROPERTY) + ifaceMethod->Id()->Name().Mutf8(), ctx->allocator);
166         auto *field = CreateAnonClassField(ctx, copyIfaceMethod, anonClassFieldName);
167         if (field->IsReadonly()) {
168             readonlyFields.push_back(
169                 std::make_tuple(anonClassFieldName, ifaceMethod->Id()->Name(), field->TypeAnnotation()->TsType()));
170         }
171         classBody->push_back(field);
172         SetSourceRangesRecursively(field, ifaceMethod->Range());
173 
174         auto *getter = CreateAnonClassFieldGetterSetter(ctx, copyIfaceMethod, false, anonClassFieldName);
175         classBody->push_back(getter);
176         SetSourceRangesRecursively(getter, ifaceMethod->Range());
177 
178         if (copyIfaceMethod->Overloads().size() == 1 && copyIfaceMethod->Overloads()[0]->Function()->IsSetter()) {
179             auto *setter = CreateAnonClassFieldGetterSetter(ctx, copyIfaceMethod, true, anonClassFieldName);
180             classBody->push_back(setter);
181             SetSourceRangesRecursively(setter, ifaceMethod->Range());
182         }
183     }
184 }
185 
186 // CC-OFFNXT(G.FUN.01-CPP) solid logic
FillAnonClassBody(public_lib::Context * ctx,ArenaVector<ir::AstNode * > * classBody,ir::TSInterfaceDeclaration * ifaceNode,ArenaVector<ReadonlyFieldHolder> & readonlyFields,checker::ETSObjectType * interfaceType=nullptr)187 static void FillAnonClassBody(public_lib::Context *ctx, ArenaVector<ir::AstNode *> *classBody,
188                               ir::TSInterfaceDeclaration *ifaceNode, ArenaVector<ReadonlyFieldHolder> &readonlyFields,
189                               checker::ETSObjectType *interfaceType = nullptr)
190 {
191     FillClassBody(ctx, classBody, ifaceNode->Body()->Body(), readonlyFields, interfaceType);
192     for (auto *extendedIface : ifaceNode->TsType()->AsETSObjectType()->Interfaces()) {
193         FillAnonClassBody(ctx, classBody, extendedIface->GetDeclNode()->AsTSInterfaceDeclaration(), readonlyFields,
194                           extendedIface);
195     }
196 }
197 
198 // Annotate synthetic class so we can determite it's origin in a runtime
199 // Now implemented for the anon class generated from an interface only
AnnotateGeneratedAnonClass(checker::ETSChecker * checker,ir::ClassDefinition * classDef)200 static void AnnotateGeneratedAnonClass(checker::ETSChecker *checker, ir::ClassDefinition *classDef)
201 {
202     auto *annoId =
203         checker->ProgramAllocNode<ir::Identifier>(Signatures::INTERFACE_OBJ_LITERAL, checker->ProgramAllocator());
204     annoId->SetAnnotationUsage();
205     auto *annoUsage = checker->ProgramAllocNode<ir::AnnotationUsage>(annoId, checker->ProgramAllocator());
206     ES2PANDA_ASSERT(annoUsage);
207     annoUsage->AddModifier(ir::ModifierFlags::ANNOTATION_USAGE);
208     annoUsage->SetParent(classDef);
209     annoId->SetParent(annoUsage);
210     classDef->Annotations().emplace_back(annoUsage);
211     RefineSourceRanges(annoUsage);
212     CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, annoUsage);
213 }
214 
GenerateAnonClassTypeFromInterface(public_lib::Context * ctx,ir::TSInterfaceDeclaration * ifaceNode)215 static void GenerateAnonClassTypeFromInterface(public_lib::Context *ctx, ir::TSInterfaceDeclaration *ifaceNode)
216 {
217     auto *checker = ctx->checker->AsETSChecker();
218 
219     if (ifaceNode->GetAnonClass() != nullptr) {
220         return;
221     }
222 
223     auto classBodyBuilder = [ctx, checker, ifaceNode](ArenaVector<ir::AstNode *> *classBody) {
224         if (ifaceNode->TsType() == nullptr) {
225             ifaceNode->Check(checker);
226         }
227         ArenaVector<ReadonlyFieldHolder> readonlyFields(ctx->Allocator()->Adapter());
228         FillAnonClassBody(ctx, classBody, ifaceNode, readonlyFields);
229         classBody->push_back(CreateAnonClassImplCtor(ctx, readonlyFields));
230     };
231 
232     auto originalName = std::string {ifaceNode->InternalName()};
233     std::replace(originalName.begin(), originalName.end(), '.', '$');
234     auto anonClassName = util::UString(originalName.append(OBJECT_LITERAL_SUFFIX), checker->ProgramAllocator());
235     auto *classDecl = checker->BuildClass(anonClassName.View(), classBodyBuilder);
236     RefineSourceRanges(classDecl);
237     auto *classDef = classDecl->Definition();
238     auto *classType = classDef->TsType()->AsETSObjectType();
239     classDef->SetAnonymousModifier();
240 
241     classDecl->SetRange(ifaceNode->Range());
242     classDef->SetRange(ifaceNode->Range());
243 
244     AnnotateGeneratedAnonClass(checker, classDef);
245 
246     // Class type params
247     if (ifaceNode->TypeParams() != nullptr) {
248         ArenaVector<checker::Type *> typeArgs(ctx->Allocator()->Adapter());
249         for (auto param : ifaceNode->TypeParams()->Params()) {
250             auto *var = param->Name()->Variable();
251             ES2PANDA_ASSERT(var && var->TsType()->IsETSTypeParameter());
252             typeArgs.push_back(var->TsType());
253         }
254         classType->SetTypeArguments(std::move(typeArgs));
255     }
256 
257     // Class implements
258     auto *classImplements = ctx->AllocNode<ir::TSClassImplements>(
259         ctx->AllocNode<ir::OpaqueTypeNode>(ifaceNode->TsType(), ctx->Allocator()));
260     ES2PANDA_ASSERT(classImplements);
261     classImplements->SetParent(classDef);
262     classDef->Implements().emplace_back(classImplements);
263     classType->RemoveObjectFlag(checker::ETSObjectFlags::RESOLVED_INTERFACES);
264     checker->GetInterfacesOfClass(classType);
265 
266     ifaceNode->SetAnonClass(classDecl);
267 }
268 
GenerateAnonClassTypeFromAbstractClass(public_lib::Context * ctx,ir::ClassDefinition * abstractClassNode)269 static void GenerateAnonClassTypeFromAbstractClass(public_lib::Context *ctx, ir::ClassDefinition *abstractClassNode)
270 {
271     auto *checker = ctx->checker->AsETSChecker();
272 
273     if (abstractClassNode->GetAnonClass() != nullptr) {
274         return;
275     }
276 
277     auto classBodyBuilder = [checker](ArenaVector<ir::AstNode *> *classBody) {
278         checker::ETSChecker::ClassInitializerBuilder initBuilder =
279             [checker]([[maybe_unused]] ArenaVector<ir::Statement *> *statements,
280                       [[maybe_unused]] ArenaVector<ir::Expression *> *params) {
281                 checker->AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
282             };
283 
284         auto ctor = checker->CreateClassInstanceInitializer(initBuilder);
285         classBody->push_back(ctor);
286     };
287 
288     auto originalName = std::string {abstractClassNode->InternalName()};
289     std::replace(originalName.begin(), originalName.end(), '.', '$');
290     auto anonClassName = util::UString(originalName.append(OBJECT_LITERAL_SUFFIX), checker->ProgramAllocator());
291     auto *classDecl = checker->BuildClass(anonClassName.View(), classBodyBuilder);
292     RefineSourceRanges(classDecl);
293     auto *classDef = classDecl->Definition();
294     auto *classType = classDef->TsType()->AsETSObjectType();
295 
296     classDecl->SetRange(abstractClassNode->Range());
297     classDef->SetAnonymousModifier();
298     classDef->SetRange(abstractClassNode->Range());
299 
300     // Class type params
301     if (abstractClassNode->TypeParams() != nullptr) {
302         ArenaVector<checker::Type *> typeArgs(ctx->Allocator()->Adapter());
303         for (auto param : abstractClassNode->TypeParams()->Params()) {
304             auto *var = param->Name()->Variable();
305             ES2PANDA_ASSERT(var && var->TsType()->IsETSTypeParameter());
306             typeArgs.push_back(var->TsType());
307         }
308         classType->SetTypeArguments(std::move(typeArgs));
309     }
310 
311     abstractClassNode->SetAnonClass(classDecl);
312     classType->SetSuperType(abstractClassNode->TsType()->AsETSObjectType());
313 }
314 
ProcessDeclNode(checker::ETSChecker * checker,checker::ETSObjectType * targetType,ir::ObjectExpression * objExpr)315 static checker::Type *ProcessDeclNode(checker::ETSChecker *checker, checker::ETSObjectType *targetType,
316                                       ir::ObjectExpression *objExpr)
317 {
318     auto *declNode = targetType->GetDeclNode();
319 
320     if (declNode->IsTSInterfaceDeclaration()) {
321         auto *ifaceNode = declNode->AsTSInterfaceDeclaration();
322         if (ifaceNode->GetAnonClass() == nullptr) {
323             checker->LogError(diagnostic::INTERFACE_WITH_METHOD, {}, objExpr->Start());
324             return checker->GlobalTypeError();
325         }
326         return ifaceNode->GetAnonClass()->Definition()->TsType();
327     }
328 
329     auto *classDef = declNode->AsClassDefinition();
330     ES2PANDA_ASSERT(classDef->IsAbstract());
331 
332     if (classDef->GetAnonClass() == nullptr) {
333         for (auto it : classDef->Body()) {
334             if (!it->IsMethodDefinition() || !it->AsMethodDefinition()->IsAbstract()) {
335                 continue;
336             }
337 
338             ES2PANDA_ASSERT(it->AsMethodDefinition()->Id());
339             checker->LogError(diagnostic::ABSTRACT_METH_IN_ABSTRACT_CLASS, {it->AsMethodDefinition()->Id()->Name()},
340                               objExpr->Start());
341             return checker->GlobalTypeError();
342         }
343     }
344     return classDef->GetAnonClass()->Definition()->TsType();
345 }
346 
HandleInterfaceLowering(public_lib::Context * ctx,ir::ObjectExpression * objExpr)347 static void HandleInterfaceLowering(public_lib::Context *ctx, ir::ObjectExpression *objExpr)
348 {
349     auto *checker = ctx->checker->AsETSChecker();
350     auto *targetType = objExpr->TsType();
351     checker->CheckObjectLiteralKeys(objExpr->Properties());
352 
353     auto *etsTargetType = targetType->AsETSObjectType();
354     checker::Type *resultType = ProcessDeclNode(checker, etsTargetType, objExpr);
355 
356     if (resultType->IsTypeError()) {
357         objExpr->SetTsType(resultType);
358         return;
359     }
360 
361     if (etsTargetType->IsPartial()) {
362         resultType->AsETSObjectType()->SetBaseType(etsTargetType->GetBaseType());
363     }
364 
365     if (!etsTargetType->TypeArguments().empty()) {
366         ArenaVector<checker::Type *> typeArgTypes(etsTargetType->TypeArguments());
367         checker::InstantiationContext instantiationCtx(checker, resultType->AsETSObjectType(), std::move(typeArgTypes),
368                                                        objExpr->Start());
369         resultType = instantiationCtx.Result();
370     }
371 
372     if (const auto *const parent = objExpr->Parent(); parent->IsArrayExpression()) {
373         for (auto *elem : parent->AsArrayExpression()->Elements()) {
374             if (elem->IsObjectExpression()) {
375                 elem->AsObjectExpression()->SetTsType(resultType);
376             }
377         }
378     }
379     objExpr->SetTsType(resultType);
380 }
381 
CheckInterfaceShouldGenerateAnonClass(ir::TSInterfaceDeclaration * interfaceDecl)382 static bool CheckInterfaceShouldGenerateAnonClass(ir::TSInterfaceDeclaration *interfaceDecl)
383 {
384     for (auto it : interfaceDecl->Body()->Body()) {
385         ES2PANDA_ASSERT(it->IsMethodDefinition());
386         auto methodDef = it->AsMethodDefinition();
387         ES2PANDA_ASSERT(methodDef->Function());
388         if (!methodDef->Function()->IsGetter() && !methodDef->Function()->IsSetter()) {
389             return false;
390         }
391     }
392 
393     return true;
394 }
395 
CheckAbstractClassShouldGenerateAnonClass(ir::ClassDefinition * classDef)396 static bool CheckAbstractClassShouldGenerateAnonClass(ir::ClassDefinition *classDef)
397 {
398     auto constructorSigs = classDef->TsType()->AsETSObjectType()->ConstructSignatures();
399     if (auto res = std::find_if(constructorSigs.cbegin(), constructorSigs.cend(),
400                                 [](checker::Signature *sig) -> bool { return sig->MinArgCount() == 0; });
401         res == constructorSigs.cend()) {
402         return false;
403     }
404     for (auto it : classDef->Body()) {
405         if (it->IsMethodDefinition() && it->AsMethodDefinition()->IsAbstract()) {
406             return false;
407         }
408     }
409 
410     return true;
411 }
412 
TransfromInterfaceDecl(public_lib::Context * ctx,parser::Program * program)413 static void TransfromInterfaceDecl(public_lib::Context *ctx, parser::Program *program)
414 {
415     program->Ast()->IterateRecursivelyPostorder([ctx, program](ir::AstNode *ast) -> void {
416         if (ast->IsTSInterfaceDeclaration() && CheckInterfaceShouldGenerateAnonClass(ast->AsTSInterfaceDeclaration())) {
417             GenerateAnonClassTypeFromInterface(ctx, ast->AsTSInterfaceDeclaration());
418         } else if (ast->IsClassDefinition() && ast != program->GlobalClass() &&
419                    ast->AsClassDefinition()->IsAbstract() &&
420                    CheckAbstractClassShouldGenerateAnonClass(ast->AsClassDefinition())) {
421             GenerateAnonClassTypeFromAbstractClass(ctx, ast->AsClassDefinition());
422         }
423     });
424 }
425 
TransfromInterfaceLiteral(public_lib::Context * ctx,parser::Program * program)426 static void TransfromInterfaceLiteral(public_lib::Context *ctx, parser::Program *program)
427 {
428     program->Ast()->IterateRecursivelyPostorder([ctx](ir::AstNode *ast) -> void {
429         if (ast->IsObjectExpression() && (IsInterfaceType(ast->AsObjectExpression()->TsType()) ||
430                                           IsAbstractClassType(ast->AsObjectExpression()->TsType()))) {
431             HandleInterfaceLowering(ctx, ast->AsObjectExpression());
432         }
433     });
434 }
435 
Perform(public_lib::Context * ctx,parser::Program * program)436 bool InterfaceObjectLiteralLowering::Perform(public_lib::Context *ctx, parser::Program *program)
437 {
438     auto *varbinder = program->VarBinder()->AsETSBinder();
439     for (auto &[_, extPrograms] : program->ExternalSources()) {
440         (void)_;
441         for (auto *extProg : extPrograms) {
442             auto *savedProgram = varbinder->Program();
443             auto *savedRecordTable = varbinder->GetRecordTable();
444             auto *savedTopScope = varbinder->TopScope();
445             varbinder->ResetTopScope(extProg->GlobalScope());
446             varbinder->SetRecordTable(varbinder->GetExternalRecordTable().at(extProg));
447             varbinder->SetProgram(extProg);
448             TransfromInterfaceDecl(ctx, extProg);
449             varbinder->SetProgram(savedProgram);
450             varbinder->SetRecordTable(savedRecordTable);
451             varbinder->ResetTopScope(savedTopScope);
452         }
453     }
454 
455     TransfromInterfaceDecl(ctx, program);
456 
457     for (auto &[_, extPrograms] : program->ExternalSources()) {
458         (void)_;
459         for (auto *extProg : extPrograms) {
460             TransfromInterfaceLiteral(ctx, extProg);
461         }
462     }
463 
464     TransfromInterfaceLiteral(ctx, program);
465 
466     return true;
467 }
468 
469 }  // namespace ark::es2panda::compiler
470