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, ®Spiller, 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