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