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