1/**
2 * Copyright (c) 2021-2024 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
18#include "ast_verifier/ASTVerifier.h"
19#include "es2panda.h"
20#include "checker/ETSAnalyzer.h"
21#include "checker/TSAnalyzer.h"
22#include "compiler/core/compileQueue.h"
23#include "compiler/core/compilerImpl.h"
24#include "compiler/core/pandagen.h"
25#include "compiler/core/ETSCompiler.h"
26#include "compiler/core/ETSGen.h"
27#include "compiler/core/JSCompiler.h"
28#include "compiler/core/JSemitter.h"
29#include "compiler/core/ETSemitter.h"
30#include "compiler/lowering/phase.h"
31#include "evaluate/scopedDebugInfoPlugin.h"
32#include "parser/parserImpl.h"
33#include "parser/JSparser.h"
34#include "parser/ASparser.h"
35#include "parser/TSparser.h"
36#include "parser/ETSparser.h"
37#include "parser/program/program.h"
38#include "varbinder/JSBinder.h"
39#include "varbinder/ASBinder.h"
40#include "varbinder/TSBinder.h"
41#include "varbinder/ETSBinder.h"
42#include "checker/TSchecker.h"
43#include "checker/ETSchecker.h"
44#include "checker/ASchecker.h"
45#include "checker/JSchecker.h"
46#include "public/public.h"
47
48namespace ark::es2panda::compiler {
49
50void CompilerImpl::HandleContextLiterals(public_lib::Context *context)
51{
52    auto *emitter = context->emitter;
53
54    uint32_t index = 0;
55    for (const auto &buff : context->contextLiterals) {
56        emitter->AddLiteralBuffer(buff, index++);
57    }
58
59    emitter->LiteralBufferIndex() += context->contextLiterals.size();
60}
61
62ark::pandasm::Program *CompilerImpl::Emit(public_lib::Context *context)
63{
64    HandleContextLiterals(context);
65
66    queue_.Schedule(context);
67
68    /* Main thread can also be used instead of idling */
69    queue_.Consume();
70    auto *emitter = context->emitter;
71    queue_.Wait([emitter](CompileJob *job) { emitter->AddProgramElement(job->GetProgramElement()); });
72
73    return emitter->Finalize(context->config->options->CompilerOptions().dumpDebugInfo, Signatures::ETS_GLOBAL);
74}
75
76class ASTVerificationRunner {
77public:
78    class Result {
79    public:
80        explicit Result(JsonArrayBuilder &&warnings, JsonArrayBuilder &&errors)
81            : warnings_ {std::move(warnings)}, errors_ {std::move(errors)}
82        {
83        }
84
85        JsonArrayBuilder &&Warnings()
86        {
87            return std::move(warnings_);
88        }
89
90        JsonArrayBuilder &&Errors()
91        {
92            return std::move(errors_);
93        }
94
95    private:
96        JsonArrayBuilder warnings_;
97        JsonArrayBuilder errors_;
98    };
99
100    using AstPath = std::string;
101    using PhaseName = std::string;
102    using Source = std::tuple<AstPath, PhaseName>;
103    using AstToCheck = ArenaMap<AstPath, const ir::AstNode *>;
104    using GroupedMessages = std::map<Source, ast_verifier::Messages>;
105
106    ASTVerificationRunner(ArenaAllocator &allocator, const public_lib::Context &context)
107        : checkFullProgram_ {context.config->options->CompilerOptions().verifierFullProgram},
108          verifier_ {&allocator},
109          treatAsWarnings_ {context.config->options->CompilerOptions().verifierWarnings},
110          treatAsErrors_ {context.config->options->CompilerOptions().verifierErrors}
111    {
112    }
113
114    void Verify(const AstToCheck &astToCheck, const PhaseName &phaseName,
115                const ast_verifier::InvariantNameSet &accumulatedChecks)
116    {
117        for (const auto &[sourceName, ast] : astToCheck) {
118            const auto source = Source(sourceName, phaseName);
119            auto messages = verifier_.Verify(ast, accumulatedChecks);
120            auto &sourcedReport = report_[source];
121            std::copy(messages.begin(), messages.end(), std::back_inserter(sourcedReport));
122        }
123    }
124
125    Result DumpMessages()
126    {
127        auto warnings = JsonArrayBuilder {};
128        auto errors = JsonArrayBuilder {};
129        const auto filterMessages = [this, &warnings, &errors](const ast_verifier::CheckMessage &message,
130                                                               const std::string &sourceName,
131                                                               const std::string &phaseName) {
132            auto invariant = message.Invariant();
133            if (auto found = treatAsWarnings_.find(invariant); found != treatAsWarnings_.end()) {
134                warnings.Add(message.DumpJSON(ast_verifier::CheckSeverity::WARNING, sourceName, phaseName));
135                return;
136            }
137            if (auto found = treatAsErrors_.find(invariant); found != treatAsErrors_.end()) {
138                errors.Add(message.DumpJSON(ast_verifier::CheckSeverity::ERROR, sourceName, phaseName));
139            }
140        };
141
142        for (const auto &[source, messages] : report_) {
143            const auto &[sourceName, phaseName] = source;
144            for (const auto &message : messages) {
145                filterMessages(message, sourceName, phaseName);
146            }
147        }
148
149        return Result {std::move(warnings), std::move(errors)};
150    }
151
152    ASTVerificationRunner::AstToCheck ExtractAst(const parser::Program &p)
153    {
154        auto &allocator = *p.Allocator();
155        auto astToCheck = ASTVerificationRunner::AstToCheck {allocator.Adapter()};
156        astToCheck.insert(std::make_pair(p.SourceFilePath(), p.Ast()));
157        if (checkFullProgram_) {
158            for (const auto &externalSource : p.ExternalSources()) {
159                for (auto *external : externalSource.second) {
160                    astToCheck.insert(std::make_pair(external->SourceFilePath(), external->Ast()));
161                }
162            }
163        }
164        return astToCheck;
165    }
166
167private:
168    bool checkFullProgram_;
169    GroupedMessages report_;
170    ast_verifier::ASTVerifier verifier_;
171    std::unordered_set<std::string> treatAsWarnings_;
172    std::unordered_set<std::string> treatAsErrors_;
173};
174
175template <typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter, typename AstCompiler>
176static public_lib::Context::CodeGenCb MakeCompileJob()
177{
178    return [](public_lib::Context *context, varbinder::FunctionScope *scope,
179              compiler::ProgramElement *programElement) -> void {
180        RegSpiller regSpiller;
181        ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
182        AstCompiler astcompiler;
183        CodeGen cg(&allocator, &regSpiller, context, std::make_tuple(scope, programElement, &astcompiler));
184        FunctionEmitter funcEmitter(&cg, programElement);
185        funcEmitter.Generate();
186    };
187}
188
189#ifndef NDEBUG
190
191static bool RunVerifierAndPhases(CompilerImpl *compilerImpl, public_lib::Context &context,
192                                 const std::vector<Phase *> &phases, parser::Program &program)
193{
194    auto runner = ASTVerificationRunner(*context.allocator, context);
195    auto verificationCtx = ast_verifier::VerificationContext {};
196    const auto runAllChecks = context.config->options->CompilerOptions().verifierAllChecks;
197
198    for (auto *phase : phases) {
199        if (!phase->Apply(&context, &program)) {
200            compilerImpl->SetIsAnyError(context.checker->ErrorLogger()->IsAnyError() ||
201                                        context.parser->ErrorLogger()->IsAnyError());
202            return false;
203        }
204
205        if (runAllChecks) {
206            auto ast = runner.ExtractAst(program);
207            runner.Verify(ast, std::string {phase->Name()}, verificationCtx.AccumulatedChecks());
208        }
209        verificationCtx.IntroduceNewInvariants(phase->Name());
210    }
211
212    if (!runAllChecks) {
213        auto ast = runner.ExtractAst(program);
214        runner.Verify(ast, "AfterAllPhases", verificationCtx.AccumulatedChecks());
215    }
216
217    auto result = runner.DumpMessages();
218    if (auto warnings = result.Warnings().Build(); warnings != "[]") {
219        LOG(WARNING, ES2PANDA) << warnings;
220    }
221
222    if (auto errors = result.Errors().Build(); errors != "[]") {
223        ASSERT_PRINT(false, errors);
224    }
225
226    return true;
227}
228#endif
229
230static bool RunPhases(CompilerImpl *compilerImpl, public_lib::Context &context, const std::vector<Phase *> &phases,
231                      parser::Program &program)
232{
233    for (auto *phase : phases) {
234        if (!phase->Apply(&context, &program)) {
235            compilerImpl->SetIsAnyError(context.checker->ErrorLogger()->IsAnyError() ||
236                                        context.parser->ErrorLogger()->IsAnyError());
237            return false;
238        }
239    }
240    return true;
241}
242
243static void CreateDebuggerEvaluationPlugin(checker::ETSChecker &checker, ArenaAllocator &allocator,
244                                           parser::Program *program, const CompilerOptions &options)
245{
246    // Sometimes evaluation mode might work without project context.
247    // In this case, users might omit context files.
248    if (options.debuggerEvalMode && !options.debuggerEvalPandaFiles.empty()) {
249        auto *plugin = allocator.New<evaluate::ScopedDebugInfoPlugin>(program, &checker, options);
250        checker.SetDebugInfoPlugin(plugin);
251    }
252}
253
254using EmitCb = std::function<pandasm::Program *(public_lib::Context *)>;
255using PhaseListGetter = std::function<std::vector<compiler::Phase *>(ScriptExtension)>;
256
257static bool ParserErrorChecker(bool isAnyError, parser::Program *program, CompilerImpl *compilerImpl,
258                               const CompilationUnit &unit)
259{
260    if (isAnyError) {
261        compilerImpl->SetIsAnyError(isAnyError);
262        if (unit.options.CompilerOptions().dumpAst) {
263            std::cout << program->Dump() << std::endl;
264        }
265        return false;
266    }
267    return true;
268}
269
270template <typename Parser, typename VarBinder, typename Checker, typename Analyzer, typename AstCompiler,
271          typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter>
272static pandasm::Program *CreateCompiler(const CompilationUnit &unit, const PhaseListGetter &getPhases,
273                                        CompilerImpl *compilerImpl)
274{
275    ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
276    auto program = parser::Program::NewProgram<VarBinder>(&allocator);
277    program.MarkEntry();
278    auto parser =
279        Parser(&program, unit.options.CompilerOptions(), static_cast<parser::ParserStatus>(unit.rawParserStatus));
280    auto checker = Checker();
281    auto analyzer = Analyzer(&checker);
282    checker.SetAnalyzer(&analyzer);
283
284    auto *varbinder = program.VarBinder();
285    varbinder->SetProgram(&program);
286
287    if constexpr (std::is_same_v<Checker, checker::ETSChecker>) {
288        CreateDebuggerEvaluationPlugin(checker, allocator, &program, unit.options.CompilerOptions());
289    }
290
291    public_lib::Context context;
292
293    auto config = public_lib::ConfigImpl {};
294    context.config = &config;
295    context.config->options = &unit.options;
296    context.sourceFile = &unit.input;
297    context.allocator = &allocator;
298    context.queue = compilerImpl->Queue();
299    context.plugins = &compilerImpl->Plugins();
300    context.parser = &parser;
301    context.checker = &checker;
302    context.analyzer = checker.GetAnalyzer();
303    context.parserProgram = &program;
304    context.codeGenCb = MakeCompileJob<CodeGen, RegSpiller, FunctionEmitter, Emitter, AstCompiler>();
305
306    auto emitter = Emitter(&context);
307    context.emitter = &emitter;
308
309    varbinder->SetContext(&context);
310
311    parser.ParseScript(unit.input, unit.options.CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB);
312    if (!ParserErrorChecker(parser.ErrorLogger()->IsAnyError(), &program, compilerImpl, unit)) {
313        return nullptr;
314    }
315#ifndef NDEBUG
316    if (unit.ext == ScriptExtension::ETS) {
317        if (!RunVerifierAndPhases(compilerImpl, context, getPhases(unit.ext), program)) {
318            return nullptr;
319        }
320    } else if (!RunPhases(compilerImpl, context, getPhases(unit.ext), program)) {
321        return nullptr;
322    }
323#else
324    if (!RunPhases(compilerImpl, context, getPhases(unit.ext), program)) {
325        return nullptr;
326    }
327#endif
328
329    emitter.GenAnnotation();
330
331    return compilerImpl->Emit(&context);
332}
333
334pandasm::Program *CompilerImpl::Compile(const CompilationUnit &unit)
335{
336    switch (unit.ext) {
337        case ScriptExtension::TS: {
338            return CreateCompiler<parser::TSParser, varbinder::TSBinder, checker::TSChecker, checker::TSAnalyzer,
339                                  compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller,
340                                  compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, compiler::GetPhaseList, this);
341        }
342        case ScriptExtension::AS: {
343            return CreateCompiler<parser::ASParser, varbinder::ASBinder, checker::ASChecker, checker::TSAnalyzer,
344                                  compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller,
345                                  compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, compiler::GetPhaseList, this);
346        }
347        case ScriptExtension::ETS: {
348            return CreateCompiler<parser::ETSParser, varbinder::ETSBinder, checker::ETSChecker, checker::ETSAnalyzer,
349                                  compiler::ETSCompiler, compiler::ETSGen, compiler::StaticRegSpiller,
350                                  compiler::ETSFunctionEmitter, compiler::ETSEmitter>(unit, compiler::GetPhaseList,
351                                                                                      this);
352        }
353        case ScriptExtension::JS: {
354            return CreateCompiler<parser::JSParser, varbinder::JSBinder, checker::JSChecker, checker::TSAnalyzer,
355                                  compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller,
356                                  compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, compiler::GetPhaseList, this);
357        }
358        default: {
359            UNREACHABLE();
360            return nullptr;
361        }
362    }
363}
364
365void CompilerImpl::DumpAsm(const ark::pandasm::Program *prog)
366{
367    Emitter::DumpAsm(prog);
368}
369
370std::string CompilerImpl::GetPhasesList(const ScriptExtension ext)
371{
372    std::stringstream ss;
373    for (auto phase : compiler::GetPhaseList(ext)) {
374        ss << " " << phase->Name() << std::endl;
375    }
376    return ss.str();
377}
378
379}  // namespace ark::es2panda::compiler
380