1/**
2 * Copyright (c) 2021 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 "es2panda.h"
17
18#include <iostream>
19
20#include "utils/timers.h"
21
22#include <compiler/core/compileQueue.h>
23#include <compiler/core/compilerContext.h>
24#include <compiler/core/compilerImpl.h>
25#include <compiler/core/emitter/emitter.h>
26#include <parser/parserImpl.h>
27#include <parser/program/program.h>
28#include <parser/transformer/transformer.h>
29#include <typescript/checker.h>
30#include <util/commonUtil.h>
31#include <util/helpers.h>
32
33namespace panda::es2panda {
34// Compiler
35
36constexpr size_t DEFAULT_THREAD_COUNT = 2;
37size_t Compiler::expectedProgsCount_ = 0;
38
39Compiler::Compiler(ScriptExtension ext) : Compiler(ext, DEFAULT_THREAD_COUNT) {}
40
41Compiler::Compiler(ScriptExtension ext, size_t threadCount)
42    : parser_(new parser::ParserImpl(ext)), compiler_(new compiler::CompilerImpl(threadCount)),
43    abcToAsmCompiler_(new panda::abc2program::Abc2ProgramCompiler)
44{
45    if (parser_->Extension() == ScriptExtension::TS) {
46        transformer_ = std::make_unique<parser::Transformer>(parser_->Allocator());
47    }
48}
49
50Compiler::~Compiler()
51{
52    delete parser_;
53    delete compiler_;
54    delete abcToAsmCompiler_;
55}
56
57panda::pandasm::Program *CreateJsonContentProgram(std::string src, std::string rname, util::PatchFix *patchFixHelper)
58{
59    panda::es2panda::compiler::CompilerContext context(nullptr, false, false, false, true, false,
60                                                       src, "", util::StringView(rname), patchFixHelper);
61    context.GetEmitter()->GenRecordNameInfo();
62    return context.GetEmitter()->Finalize(false, nullptr);
63}
64
65void Compiler::CheckOptionsAndFileForAbcInput(const std::string &fname, const CompilerOptions &options)
66{
67    if (!options.enableAbcInput) {
68        throw Error(ErrorType::GENERIC, "\"--enable-abc-input\" is not enabled, abc file " + fname +
69            "could not be used as the input.");
70    }
71    if (options.targetApiVersion < util::Helpers::ABC_TO_PROGRAM_MIN_SUPPORTED_API_VERSION) {
72        throw Error(ErrorType::GENERIC, "Target api version '" + std::to_string(options.targetApiVersion) +
73                    "' should be greater than or equal to '" +
74                    std::to_string(util::Helpers::ABC_TO_PROGRAM_MIN_SUPPORTED_API_VERSION) + "'.");
75    }
76    if (!options.mergeAbc && options.sourceFiles.size() != 1) {
77        throw Error(ErrorType::GENERIC, "If an abc file is used as input, it must be the only input file "
78                    "when the option '--merge-abc' is not enabled.");
79    }
80    if (!abcToAsmCompiler_->OpenAbcFile(fname)) {
81        throw Error(ErrorType::GENERIC, "Open abc file " + fname + " failed.");
82    }
83    if (!abcToAsmCompiler_->CheckFileVersionIsSupported(util::Helpers::ABC_TO_PROGRAM_MIN_SUPPORTED_BYTECODE_VERSION,
84                                                        options.targetApiVersion, options.targetApiSubVersion)) {
85        throw Error(ErrorType::GENERIC, "The input abc file '" + fname + "' owns a higher api version or a higher " +
86                    "sdkReleaseType compared to current compilation process.");
87    }
88}
89
90panda::pandasm::Program *Compiler::CompileAbcFile(const std::string &fname, const CompilerOptions &options)
91{
92    try {
93        CheckOptionsAndFileForAbcInput(fname, options);
94        return abcToAsmCompiler_->CompileAbcFile();
95    } catch (const class Error &e) {
96        std::cerr << e.TypeString() << ": " << e.Message();
97        std::cerr << " [" << fname << "]" << std::endl;
98        throw;
99    }
100}
101
102void Compiler::CompileAbcFileInParallel(SourceFile *src, const CompilerOptions &options,
103                                        std::map<std::string, panda::es2panda::util::ProgramCache*> &progsInfo,
104                                        panda::ArenaAllocator *allocator)
105{
106    try {
107        CheckOptionsAndFileForAbcInput(src->fileName, options);
108    } catch (const class Error &e) {
109        std::cerr << e.TypeString() << ": " << e.Message();
110        std::cerr << " [" << src->fileName << "]" << std::endl;
111        throw;
112    }
113
114    auto *compileAbcClassQueue = new compiler::CompileAbcClassQueue(options.abcClassThreadCount,
115                                                                    options,
116                                                                    *abcToAsmCompiler_,
117                                                                    progsInfo,
118                                                                    allocator,
119                                                                    src);
120    try {
121        compileAbcClassQueue->Schedule();
122        compileAbcClassQueue->Consume();
123        compileAbcClassQueue->Wait();
124    } catch (const class Error &e) {
125        throw e;
126    }
127
128    delete compileAbcClassQueue;
129    compileAbcClassQueue = nullptr;
130}
131
132panda::pandasm::Program *Compiler::Compile(const SourceFile &input, const CompilerOptions &options,
133    util::SymbolTable *symbolTable)
134{
135    ASSERT(input.isSourceMode);
136    /* TODO(dbatyai): pass string view */
137    std::string fname(input.fileName);
138    std::string src(input.source);
139    std::string rname(input.recordName);
140    std::string sourcefile(input.sourcefile);
141    std::string pkgName(input.pkgName);
142
143    auto *patchFixHelper = InitPatchFixHelper(input, options, symbolTable);
144
145    if (fname.substr(fname.find_last_of(".") + 1) == "json") {
146        return CreateJsonContentProgram(src, rname, patchFixHelper);
147    }
148
149    try {
150        panda::Timer::timerStart(panda::EVENT_PARSE, fname);
151        auto ast = parser_->Parse(input, options);
152        ast.Binder()->SetProgram(&ast);
153
154        if (options.dumpAst) {
155            std::cout << ast.Dump() << std::endl;
156        }
157
158        ProcessAstForTS(&ast, options);
159
160        if (options.parseOnly) {
161            return nullptr;
162        }
163        panda::Timer::timerEnd(panda::EVENT_PARSE, fname);
164
165        panda::Timer::timerStart(panda::EVENT_COMPILE_TO_PROGRAM, fname);
166        std::string debugInfoSourceFile = options.debugInfoSourceFile.empty() ?
167                                          sourcefile : options.debugInfoSourceFile;
168        auto *prog = compiler_->Compile(&ast, options, debugInfoSourceFile, pkgName);
169        panda::Timer::timerEnd(panda::EVENT_COMPILE_TO_PROGRAM, fname);
170
171        CleanPatchFixHelper(patchFixHelper);
172        return prog;
173    } catch (const class Error &e) {
174        error_ = e;
175
176        CleanPatchFixHelper(patchFixHelper);
177        return nullptr;
178    }
179}
180
181util::PatchFix *Compiler::InitPatchFixHelper(const SourceFile &input, const CompilerOptions &options,
182                                             util::SymbolTable *symbolTable)
183{
184    util::PatchFix *patchFixHelper = nullptr;
185    bool needDumpSymbolFile = !options.patchFixOptions.dumpSymbolTable.empty();
186    bool needGeneratePatch = options.patchFixOptions.generatePatch && !options.patchFixOptions.symbolTable.empty();
187    bool isHotReload = options.patchFixOptions.hotReload;
188    bool isColdReload = options.patchFixOptions.coldReload;
189    bool isColdFix = options.patchFixOptions.coldFix;
190    if (symbolTable && (needDumpSymbolFile || needGeneratePatch || isHotReload || isColdReload)) {
191        util::PatchFixKind patchFixKind = util::PatchFixKind::DUMPSYMBOLTABLE;
192        if (needGeneratePatch) {
193            patchFixKind = isColdFix ? util::PatchFixKind::COLDFIX : util::PatchFixKind::HOTFIX;
194        }
195        if (isHotReload) {
196            patchFixKind = util::PatchFixKind::HOTRELOAD;
197        }
198        if (isColdReload) {
199            patchFixKind = util::PatchFixKind::COLDRELOAD;
200        }
201        patchFixHelper = new util::PatchFix(needDumpSymbolFile, needGeneratePatch, patchFixKind, input.recordName,
202            symbolTable);
203        parser_->AddPatchFixHelper(patchFixHelper);
204        compiler_->AddPatchFixHelper(patchFixHelper);
205    }
206    return patchFixHelper;
207}
208
209void Compiler::CleanPatchFixHelper(const util::PatchFix *patchFixHelper)
210{
211    if (patchFixHelper) {
212        delete patchFixHelper;
213        patchFixHelper = nullptr;
214    }
215}
216
217void Compiler::DumpAsm(const panda::pandasm::Program *prog)
218{
219    compiler::CompilerImpl::DumpAsm(prog);
220}
221
222int Compiler::CompileFiles(CompilerOptions &options,
223    std::map<std::string, panda::es2panda::util::ProgramCache*> &progsInfo, panda::ArenaAllocator *allocator)
224{
225    util::SymbolTable *symbolTable = nullptr;
226    if (!options.patchFixOptions.symbolTable.empty() || !options.patchFixOptions.dumpSymbolTable.empty()) {
227        symbolTable = new util::SymbolTable(options.patchFixOptions.symbolTable,
228            options.patchFixOptions.dumpSymbolTable);
229        if (!symbolTable->Initialize(options.targetApiVersion, options.targetApiSubVersion)) {
230            std::cerr << "Failed to initialize for Hotfix." << std::endl;
231            return 1;
232        }
233    }
234
235    bool failed = false;
236    std::unordered_set<std::string> optimizationPendingProgs;
237    auto queue = new compiler::CompileFileQueue(options.fileThreadCount, &options, progsInfo,
238                                                optimizationPendingProgs, symbolTable, allocator);
239
240    try {
241        queue->Schedule();
242        queue->Consume();
243        queue->Wait();
244    } catch (const class Error &e) {
245        if (!e.Reported()) {
246            std::cerr << e.TypeString() << ": " << e.Message() << std::endl;
247        }
248        failed = true;
249    }
250
251    delete queue;
252    queue = nullptr;
253
254    if (symbolTable) {
255        if (!options.patchFixOptions.dumpSymbolTable.empty()) {
256            symbolTable->WriteSymbolTable();
257        }
258        delete symbolTable;
259        symbolTable = nullptr;
260    }
261
262    if (options.requireGlobalOptimization) {
263        auto postAnalysisOptimizeQueue = new compiler::PostAnalysisOptimizeFileQueue(options.fileThreadCount,
264                                                                                     progsInfo,
265                                                                                     optimizationPendingProgs);
266        try {
267            postAnalysisOptimizeQueue->Schedule();
268            postAnalysisOptimizeQueue->Consume();
269            postAnalysisOptimizeQueue->Wait();
270        } catch (const class Error &e) {
271            // Optimization failed, but the program can still be used as unoptimized
272        }
273        delete postAnalysisOptimizeQueue;
274    }
275
276    return failed ? 1 : 0;
277}
278
279panda::pandasm::Program *Compiler::CompileFile(const CompilerOptions &options, SourceFile *src,
280                                               util::SymbolTable *symbolTable)
281{
282    auto *program = Compile(*src, options, symbolTable);
283    if (!program) {
284        auto &err = GetError();
285        if (err.Message().empty() && options.parseOnly) {
286            return nullptr;
287        }
288
289        std::cerr << err.TypeString() << ": " << err.Message();
290        std::cerr << " [" << util::Helpers::BaseName(src->fileName) << ":"
291                  << err.Line() << ":" << err.Col() << "]" << std::endl;
292        err.SetReported(true);
293
294        throw err;
295    }
296    return program;
297}
298
299void Compiler::ProcessAstForTS(parser::Program *ast, const es2panda::CompilerOptions &options)
300{
301    if (ast->Extension() != ScriptExtension::TS) {
302        return;
303    }
304    if (options.enableTypeCheck) {
305        ArenaAllocator localAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
306        auto checker = std::make_unique<checker::Checker>(&localAllocator, ast->Binder());
307        checker->StartChecker();
308    }
309
310    transformer_->Transform(ast);
311    ast->Binder()->IdentifierAnalysis(binder::ResolveBindingFlags::TS_AFTER_TRANSFORM);
312    if (options.dumpTransformedAst) {
313        std::cout << ast->Dump() << std::endl;
314    }
315    if (options.checkTransformedAstStructure) {
316        transformer_->CheckTransformedAstStructure(ast);
317    }
318}
319
320}  // namespace panda::es2panda
321