• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 "compilerImpl.h"
17 #include <string>
18 
19 #include "es2panda.h"
20 #include "ast_verifier/ASTVerifier.h"
21 #include "checker/ETSAnalyzer.h"
22 #include "checker/TSAnalyzer.h"
23 #include "checker/TSchecker.h"
24 #include "checker/ETSchecker.h"
25 #include "checker/ASchecker.h"
26 #include "checker/JSchecker.h"
27 #include "compiler/core/compileQueue.h"
28 #include "compiler/core/compilerImpl.h"
29 #include "compiler/core/pandagen.h"
30 #include "compiler/core/ETSCompiler.h"
31 #include "compiler/core/ETSGen.h"
32 #include "compiler/core/JSCompiler.h"
33 #include "compiler/core/JSemitter.h"
34 #include "compiler/core/ETSemitter.h"
35 #include "compiler/lowering/phase.h"
36 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
37 #include "compiler/lowering/checkerPhase.h"
38 #include "compiler/lowering/resolveIdentifiers.h"
39 #include "compiler/lowering/ets/insertOptionalParametersAnnotation.h"
40 #include "evaluate/scopedDebugInfoPlugin.h"
41 #include "generated/isa.h"
42 #include "parser/parserImpl.h"
43 #include "parser/JSparser.h"
44 #include "parser/ASparser.h"
45 #include "parser/TSparser.h"
46 #include "parser/ETSparser.h"
47 #include "parser/program/program.h"
48 #include "public/public.h"
49 #include "util/ustring.h"
50 #include "varbinder/JSBinder.h"
51 #include "varbinder/ASBinder.h"
52 #include "varbinder/TSBinder.h"
53 #include "varbinder/ETSBinder.h"
54 
55 namespace ark::es2panda::compiler {
56 
HandleContextLiterals(public_lib::Context * context)57 void CompilerImpl::HandleContextLiterals(public_lib::Context *context)
58 {
59     auto *emitter = context->emitter;
60 
61     uint32_t index = 0;
62     for (const auto &buff : context->contextLiterals) {
63         emitter->AddLiteralBuffer(buff, index++);
64     }
65 
66     emitter->LiteralBufferIndex() += context->contextLiterals.size();
67 }
68 
Emit(public_lib::Context * context)69 ark::pandasm::Program *CompilerImpl::Emit(public_lib::Context *context)
70 {
71     HandleContextLiterals(context);
72 
73     queue_.Schedule(context);
74 
75     /* Main thread can also be used instead of idling */
76     queue_.Consume();
77     auto *emitter = context->emitter;
78     queue_.Wait([emitter](CompileJob *job) { emitter->AddProgramElement(job->GetProgramElement()); });
79 
80     return emitter->Finalize(context->config->options->IsDumpDebugInfo(), Signatures::ETS_GLOBAL);
81 }
82 
83 template <typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter, typename AstCompiler>
MakeCompileJob()84 static public_lib::Context::CodeGenCb MakeCompileJob()
85 {
86     return [](public_lib::Context *context, varbinder::FunctionScope *scope,
87               compiler::ProgramElement *programElement) -> void {
88         RegSpiller regSpiller;
89         ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
90         AstCompiler astcompiler;
91         CodeGen cg(&allocator, &regSpiller, context, std::make_tuple(scope, programElement, &astcompiler));
92         FunctionEmitter funcEmitter(&cg, programElement);
93         funcEmitter.Generate();
94     };
95 }
96 
CheckOptionsBeforePhase(const util::Options & options,const parser::Program & program,const std::string & name)97 static bool CheckOptionsBeforePhase(const util::Options &options, const parser::Program &program,
98                                     const std::string &name)
99 {
100     if (options.GetDumpBeforePhases().count(name) > 0U) {
101         std::cout << "Before phase " << name << ":\n";
102         std::cout << program.Dump() << std::endl;
103     }
104 
105     if (options.GetDumpEtsSrcBeforePhases().count(name) > 0U) {
106         std::cout << "Before phase " << name << " ets source:\n";
107         std::cout << program.Ast()->DumpEtsSrc() << std::endl;
108     }
109 
110     return options.GetExitBeforePhase() == name;
111 }
112 
HandleGenerateDecl(const parser::Program & program,util::DiagnosticEngine & diagnosticEngine,const std::string & outputPath,bool isIsolatedDeclgen)113 void HandleGenerateDecl(const parser::Program &program, util::DiagnosticEngine &diagnosticEngine,
114                         const std::string &outputPath, bool isIsolatedDeclgen)
115 {
116     std::ofstream outFile(outputPath);
117     if (!outFile.is_open()) {
118         diagnosticEngine.LogFatalError(diagnostic::OPEN_FAILED, util::DiagnosticMessageParams {outputPath},
119                                        lexer::SourcePosition());
120         return;
121     }
122     std::string result;
123     if (!isIsolatedDeclgen) {
124         result = program.Ast()->DumpDecl();
125     } else {
126         result = program.Ast()->IsolatedDumpDecl();
127     }
128 
129     result.erase(0, result.find_first_not_of('\n'));
130 
131     outFile << result;
132     outFile.close();
133 }
134 
CheckOptionsAfterPhase(const util::Options & options,const parser::Program & program,const std::string & name)135 static bool CheckOptionsAfterPhase(const util::Options &options, const parser::Program &program,
136                                    const std::string &name)
137 {
138     if (options.GetDumpAfterPhases().count(name) > 0U) {
139         std::cout << "After phase " << name << ":\n";
140         std::cout << program.Dump() << std::endl;
141     }
142 
143     if (options.GetDumpEtsSrcAfterPhases().count(name) > 0U) {
144         std::cout << "After phase " << name << " ets source:\n";
145         std::cout << program.Ast()->DumpEtsSrc() << std::endl;
146     }
147 
148     return options.GetExitAfterPhase() == name;
149 }
150 
DoIsolatedDeclgenCheck(const util::Options & options,const std::string & phaseName,checker::IsolatedDeclgenChecker & isolatedDeclgenChecker,public_lib::Context & context)151 static bool DoIsolatedDeclgenCheck(const util::Options &options, const std::string &phaseName,
152                                    checker::IsolatedDeclgenChecker &isolatedDeclgenChecker,
153                                    public_lib::Context &context)
154 {
155     if (!options.IsGenerateDeclEnableIsolated()) {
156         return true;
157     }
158     if (phaseName == compiler::ResolveIdentifiers::NAME) {
159         isolatedDeclgenChecker.CheckBeforeChecker();
160         if (context.diagnosticEngine->IsAnyError()) {
161             return false;
162         }
163     }
164 
165     if (phaseName == compiler::CheckerPhase::NAME) {
166         isolatedDeclgenChecker.CheckAfterChecker();
167         if (context.diagnosticEngine->IsAnyError()) {
168             return false;
169         }
170     }
171 
172     return true;
173 }
174 
RunVerifierAndPhases(public_lib::Context & context,parser::Program & program)175 static bool RunVerifierAndPhases(public_lib::Context &context, parser::Program &program)
176 {
177     auto &options = const_cast<util::Options &>(*context.config->options);
178     const auto verifierEachPhase = options.IsAstVerifierEachPhase();
179 
180     ast_verifier::ASTVerifier verifier(context, program);
181     verifier.Before();
182     checker::IsolatedDeclgenChecker isolatedDeclgenChecker(*context.diagnosticEngine, program);
183     if (options.IsGenerateDeclEnableIsolated()) {
184         options.SetGenerateDeclEnabled(true);
185     }
186 
187     bool skipPhase = false;
188     while (auto phase = context.phaseManager->NextPhase()) {
189         const auto name = std::string {phase->Name()};
190         skipPhase = options.GetSkipPhases().count(name) > 0 ||
191                     (options.IsGenerateDeclEnableIsolated() &&
192                      phase->Name() == compiler::InsertOptionalParametersAnnotation::NAME);
193         if (skipPhase) {
194             continue;
195         }
196 
197         if (options.IsGenerateDeclEnableIsolated() && name == "plugins-after-check") {
198             return false;
199         }
200 
201         if (CheckOptionsBeforePhase(options, program, name) || !phase->Apply(&context, &program) ||
202             CheckOptionsAfterPhase(options, program, name)) {
203             return false;
204         }
205 
206         if (!DoIsolatedDeclgenCheck(options, name, isolatedDeclgenChecker, context)) {
207             return false;
208         }
209 
210         if (!options.IsGenerateDeclEnableIsolated()) {
211             verifier.IntroduceNewInvariants(phase->Name());
212         }
213         if (verifierEachPhase || options.HasVerifierPhase(phase->Name())) {
214             verifier.Verify(phase->Name());
215         }
216 
217         // Stop lowerings processing after Checker phase if any error happened.
218         if (name == "plugins-after-check" && context.diagnosticEngine->IsAnyError()) {
219             return false;
220         }
221 
222         if (options.IsGenerateDeclEnabled() && name == compiler::CheckerPhase::NAME) {
223             std::string path;
224             if (!options.WasSetGenerateDeclPath()) {
225                 path = ark::os::RemoveExtension(util::BaseName(options.SourceFileName())).append(".d.ets");
226             } else {
227                 path = options.GetGenerateDeclPath();
228             }
229             HandleGenerateDecl(program, *context.diagnosticEngine, path, options.IsGenerateDeclEnableIsolated());
230         }
231     }
232 
233     verifier.After();
234     return true;
235 }
236 
RunPhases(public_lib::Context & context,parser::Program & program)237 static bool RunPhases(public_lib::Context &context, parser::Program &program)
238 {
239     const auto &options = *context.config->options;
240 
241     while (auto phase = context.phaseManager->NextPhase()) {
242         const auto name = std::string {phase->Name()};
243         if (options.GetSkipPhases().count(name) > 0) {
244             continue;
245         }
246 
247         if (CheckOptionsBeforePhase(options, program, name)) {
248             return false;
249         }
250 
251         if (!phase->Apply(&context, &program)) {
252             return false;
253         }
254 
255         if (CheckOptionsAfterPhase(options, program, name)) {
256             return false;
257         }
258     }
259 
260     return true;
261 }
262 
CreateDebuggerEvaluationPlugin(checker::ETSChecker & checker,ArenaAllocator & allocator,parser::Program * program,const util::Options & options)263 static void CreateDebuggerEvaluationPlugin(checker::ETSChecker &checker, ArenaAllocator &allocator,
264                                            parser::Program *program, const util::Options &options)
265 {
266     // Sometimes evaluation mode might work without project context.
267     // In this case, users might omit context files.
268     if (options.IsDebuggerEval() && !options.GetDebuggerEvalPandaFiles().empty()) {
269         auto *plugin = allocator.New<evaluate::ScopedDebugInfoPlugin>(program, &checker, options);
270         checker.SetDebugInfoPlugin(plugin);
271     }
272 }
273 
274 using EmitCb = std::function<pandasm::Program *(public_lib::Context *)>;
275 using PhaseListGetter = std::function<std::vector<compiler::Phase *>(ScriptExtension)>;
276 
AddExternalPrograms(public_lib::Context * ctx,const CompilationUnit & unit,parser::Program * program)277 static void AddExternalPrograms(public_lib::Context *ctx, const CompilationUnit &unit, parser::Program *program)
278 {
279     auto &extSources = program->ExternalSources();
280     for (const auto &[moduleName, extPrograms] : ctx->externalSources) {
281         for (auto *const extProg : extPrograms) {
282             if (extProg->SourceFilePath() == unit.input.filePath) {
283                 continue;
284             }
285             if (extSources.count(moduleName) == 0) {
286                 extSources.emplace(moduleName, program->Allocator()->Adapter());
287             }
288             extSources.at(moduleName).emplace_back(extProg);
289         }
290     }
291     auto varbinder = static_cast<varbinder::ETSBinder *>(ctx->transitionMemory->VarBinder());
292     auto externalRecordTable = varbinder->GetExternalRecordTable();
293     for (auto [extProg, recordTable] : externalRecordTable) {
294         if (program->SourceFilePath() == extProg->SourceFilePath()) {
295             externalRecordTable.erase(externalRecordTable.find(extProg));
296             break;
297         }
298     }
299     auto &prevGlobalTypes = ctx->transitionMemory->GlobalTypes()->GlobalTypes();
300     for (auto *gt : prevGlobalTypes) {
301         if (gt != nullptr && gt->IsETSObjectType()) {
302             auto *relation = gt->AsETSObjectType()->GetRelation();
303             if (relation != nullptr) {
304                 relation->SetChecker(ctx->checker);
305             }
306         }
307     }
308 }
309 
EmplaceProgram(public_lib::Context * ctx,util::StringView moduleName,parser::Program * extProg)310 static void EmplaceProgram(public_lib::Context *ctx, util::StringView moduleName, parser::Program *extProg)
311 {
312     auto &extSources = ctx->externalSources;
313     if (extSources.count(moduleName) == 0) {
314         extSources.emplace(moduleName, ctx->transitionMemory->PermanentAllocator()->Adapter());
315     }
316     bool found = false;
317     for (auto extSrc : extSources.at(moduleName)) {
318         if (extSrc->SourceFilePath() == extProg->SourceFilePath()) {
319             found = true;
320             break;
321         }
322     }
323     if (!found) {
324         extSources.at(moduleName).emplace_back(extProg);
325     }
326 }
327 
SavePermanents(public_lib::Context * ctx,parser::Program * program)328 static void SavePermanents(public_lib::Context *ctx, parser::Program *program)
329 {
330     for (const auto &[moduleName, extPrograms] : program->ExternalSources()) {
331         for (auto *const extProg : extPrograms) {
332             EmplaceProgram(ctx, moduleName, extProg);
333         }
334     }
335     ctx->transitionMemory->SetVarBinder(program->VarBinder());
336     auto *topScope = ctx->transitionMemory->VarBinder()->TopScope();
337     ES2PANDA_ASSERT(topScope->IsGlobalScope());
338     auto decls = topScope->Decls();
339     auto path = program->SourceFilePath();
340     // clang-format off
341     decls.erase(std::remove_if(decls.begin(), decls.end(),
342         [path](varbinder::Decl *d) -> bool {
343             auto *n = d->Node();
344             if (n == nullptr || n->Start().Program() == nullptr) {
345                 return true;
346             }
347             auto sourceFile = n->Start().Program()->SourceFilePath();
348             return sourceFile == "" || sourceFile == path;
349         }),
350         decls.end());
351     // clang-format on
352 
353     auto res = topScope->Find(util::StringView("ETSGLOBAL"));
354     auto *var = res.variable;
355     var->SetTsType(nullptr);
356     auto moduleName = std::string(program->ModuleName());
357     auto globalClassName = moduleName.substr(moduleName.find_last_of('.') + 1);
358     topScope->EraseBinding(util::StringView(globalClassName));
359 
360     std::vector<util::StringView> declaredKeys;
361     for (auto &&it : topScope->Bindings()) {
362         var = it.second;
363         if (var->Declaration() != nullptr && var->Declaration()->Node() != nullptr) {
364             auto *node = var->Declaration()->Node();
365             if (node->Range().start.Program() == program) {
366                 declaredKeys.emplace_back(it.first);
367             }
368         }
369     }
370     for (auto &&key : declaredKeys) {
371         topScope->EraseBinding(key);
372     }
373 
374     auto *varbinder = static_cast<varbinder::ETSBinder *>(program->VarBinder());
375     varbinder->GetGlobalRecordTable()->CleanUp();
376     varbinder->Functions().clear();
377 
378     ctx->transitionMemory->SetGlobalTypes(ctx->checker->GetGlobalTypesHolder());
379     ctx->transitionMemory->SetCachechedComputedAbstracts(ctx->checker->AsETSChecker()->GetCachedComputedAbstracts());
380     ctx->transitionMemory->AddCompiledProgram(ctx->parserProgram);
381 }
382 
EmitProgram(CompilerImpl * compilerImpl,public_lib::Context * context,const CompilationUnit & unit)383 static pandasm::Program *EmitProgram(CompilerImpl *compilerImpl, public_lib::Context *context,
384                                      const CompilationUnit &unit)
385 {
386     context->emitter->GenAnnotation();
387     auto result = compilerImpl->Emit(context);
388     if (unit.ext == ScriptExtension::ETS && context->compilingState != public_lib::CompilingState::SINGLE_COMPILING) {
389         SavePermanents(context, context->parserProgram);
390     }
391     return result;
392 }
393 
ExecuteParsingAndCompiling(const CompilationUnit & unit,public_lib::Context * context)394 static bool ExecuteParsingAndCompiling(const CompilationUnit &unit, public_lib::Context *context)
395 {
396     parser::Program *program = context->parserProgram;
397     if (unit.ext == ScriptExtension::ETS &&
398         context->compilingState == public_lib::CompilingState::MULTI_COMPILING_FOLLOW) {
399         AddExternalPrograms(context, unit, program);
400     }
401 
402     if (context->config->options->GetCompilationMode() == CompilationMode::GEN_ABC_FOR_EXTERNAL_SOURCE &&
403         context->config->options->GetExtension() == ScriptExtension::ETS) {
404         std::unordered_set<std::string> sourceFileNamesSet;
405         util::UString absolutePath(os::GetAbsolutePath(context->sourceFile->filePath), context->allocator);
406         sourceFileNamesSet.insert(absolutePath.View().Mutf8());
407         context->sourceFileNames.emplace_back(absolutePath.View().Utf8());
408         parser::ETSParser::AddGenExtenralSourceToParseList(context);
409         context->MarkGenAbcForExternal(sourceFileNamesSet, context->parserProgram->ExternalSources());
410     } else {
411         context->parser->ParseScript(unit.input, unit.options.GetCompilationMode() == CompilationMode::GEN_STD_LIB);
412     }
413 
414     //  We have to check the return status of 'RunVerifierAndPhase` and 'RunPhases` separately because there can be
415     //  some internal errors (say, in Post-Conditional check) or terminate options (say in 'CheckOptionsAfterPhase')
416     //  that were not reported to the log.
417     if (unit.ext == ScriptExtension::ETS) {
418         if (!RunVerifierAndPhases(*context, *program)) {
419             return false;
420         }
421     } else if (context->diagnosticEngine->IsAnyError()) {
422         if (unit.options.IsDumpAst()) {
423             std::cout << program->Dump() << std::endl;
424         }
425     } else if (!RunPhases(*context, *program)) {
426         return false;
427     }
428 
429     return !context->diagnosticEngine->IsAnyError();
430 }
431 
ClearContextAndReturnProgam(public_lib::Context * context,pandasm::Program * program)432 static pandasm::Program *ClearContextAndReturnProgam(public_lib::Context *context, pandasm::Program *program)
433 {
434     context->config = nullptr;
435     context->parser = nullptr;
436     context->checker->SetAnalyzer(nullptr);
437     context->checker = nullptr;
438     context->analyzer = nullptr;
439     context->phaseManager = nullptr;
440     context->parserProgram = nullptr;
441     context->emitter = nullptr;
442     return program;
443 }
444 
445 template <typename Parser, typename VarBinder, typename Checker, typename Analyzer, typename AstCompiler,
446           typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter>
Compile(const CompilationUnit & unit,CompilerImpl * compilerImpl,public_lib::Context * context)447 static pandasm::Program *Compile(const CompilationUnit &unit, CompilerImpl *compilerImpl, public_lib::Context *context)
448 {
449     auto config = public_lib::ConfigImpl {};
450     context->config = &config;
451     context->config->options = &unit.options;
452     context->sourceFile = &unit.input;
453     context->queue = compilerImpl->Queue();
454     context->plugins = &compilerImpl->Plugins();
455     auto program = parser::Program::NewProgram<VarBinder>(
456         context->allocator, context->compilingState == public_lib::CompilingState::MULTI_COMPILING_FOLLOW
457                                 ? context->transitionMemory->VarBinder()
458                                 : nullptr);
459     auto parser =
460         Parser(&program, unit.options, unit.diagnosticEngine, static_cast<parser::ParserStatus>(unit.rawParserStatus));
461     parser.SetContext(context);
462     context->parser = &parser;
463     auto checker = Checker(unit.diagnosticEngine, context->allocator);
464     context->checker = &checker;
465     auto analyzer = Analyzer(&checker);
466     auto phaseManager = compiler::PhaseManager(unit.ext, context->allocator);
467     checker.SetAnalyzer(&analyzer);
468     context->analyzer = checker.GetAnalyzer();
469     context->parserProgram = &program;
470     context->codeGenCb = MakeCompileJob<CodeGen, RegSpiller, FunctionEmitter, Emitter, AstCompiler>();
471     context->diagnosticEngine = &unit.diagnosticEngine;
472     context->phaseManager = &phaseManager;
473 
474     if constexpr (std::is_same_v<Checker, checker::ETSChecker>) {
475         CreateDebuggerEvaluationPlugin(checker, *context->allocator, &program, unit.options);
476         if (context->compilingState == public_lib::CompilingState::MULTI_COMPILING_FOLLOW) {
477             checker.SetCachedComputedAbstracts(context->transitionMemory->CachedComputedAbstracts());
478             checker.SetGlobalTypes(context->transitionMemory->GlobalTypes());
479             checker.AddStatus(ark::es2panda::checker::CheckerStatus::BUILTINS_INITIALIZED);
480         } else {
481             checker.InitCachedComputedAbstracts();
482         }
483     }
484     auto emitter = Emitter(context);
485     context->emitter = &emitter;
486     auto *varbinder = program.VarBinder();
487     varbinder->SetProgram(&program);
488     varbinder->SetContext(context);
489     context->checker->Initialize(varbinder);
490 
491     if (!ExecuteParsingAndCompiling(unit, context)) {
492         return ClearContextAndReturnProgam(context, nullptr);
493     }
494     return ClearContextAndReturnProgam(context, EmitProgram(compilerImpl, context, unit));
495 }
496 
Compile(const CompilationUnit & unit,public_lib::Context * context)497 pandasm::Program *CompilerImpl::Compile(const CompilationUnit &unit, public_lib::Context *context)
498 {
499     switch (unit.ext) {
500         case ScriptExtension::TS: {
501             return compiler::Compile<parser::TSParser, varbinder::TSBinder, checker::TSChecker, checker::TSAnalyzer,
502                                      compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller,
503                                      compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, this, context);
504         }
505         case ScriptExtension::AS: {
506             return compiler::Compile<parser::ASParser, varbinder::ASBinder, checker::ASChecker, checker::TSAnalyzer,
507                                      compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller,
508                                      compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, this, context);
509         }
510         case ScriptExtension::ETS: {
511             return compiler::Compile<parser::ETSParser, varbinder::ETSBinder, checker::ETSChecker, checker::ETSAnalyzer,
512                                      compiler::ETSCompiler, compiler::ETSGen, compiler::StaticRegSpiller,
513                                      compiler::ETSFunctionEmitter, compiler::ETSEmitter>(unit, this, context);
514         }
515         case ScriptExtension::JS: {
516             return compiler::Compile<parser::JSParser, varbinder::JSBinder, checker::JSChecker, checker::TSAnalyzer,
517                                      compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller,
518                                      compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, this, context);
519         }
520         default: {
521             ES2PANDA_UNREACHABLE();
522             return nullptr;
523         }
524     }
525 }
526 
DumpAsm(const ark::pandasm::Program * prog)527 void CompilerImpl::DumpAsm(const ark::pandasm::Program *prog)
528 {
529     Emitter::DumpAsm(prog);
530 }
531 
GetPhasesList(const ScriptExtension ext)532 std::string CompilerImpl::GetPhasesList(const ScriptExtension ext)
533 {
534     std::stringstream ss;
535     auto phaseManager = compiler::PhaseManager(ext, nullptr);
536     while (auto phase = phaseManager.NextPhase()) {
537         ss << " " << phase->Name() << std::endl;
538     }
539     return ss.str();
540 }
541 
542 }  // namespace ark::es2panda::compiler
543