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