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
112 auto *compileAbcClassQueue = new compiler::CompileAbcClassQueue(options.abcClassThreadCount,
113 options,
114 *abcToAsmCompiler_,
115 progsInfo,
116 allocator,
117 src);
118 try {
119 compileAbcClassQueue->Schedule();
120 compileAbcClassQueue->Consume();
121 compileAbcClassQueue->Wait();
122 } catch (const class Error &e) {
123 throw e;
124 }
125
126 delete compileAbcClassQueue;
127 compileAbcClassQueue = nullptr;
128 }
129
Compile(const SourceFile & input,const CompilerOptions & options,util::SymbolTable * symbolTable)130 panda::pandasm::Program *Compiler::Compile(const SourceFile &input, const CompilerOptions &options,
131 util::SymbolTable *symbolTable)
132 {
133 ASSERT(input.isSourceMode);
134 /* TODO(dbatyai): pass string view */
135 std::string fname(input.fileName);
136 std::string src(input.source);
137 std::string rname(input.recordName);
138 std::string sourcefile(input.sourcefile);
139 std::string pkgName(input.pkgName);
140
141 auto *patchFixHelper = InitPatchFixHelper(input, options, symbolTable);
142
143 if (fname.substr(fname.find_last_of(".") + 1) == "json") {
144 return CreateJsonContentProgram(src, rname, patchFixHelper);
145 }
146
147 try {
148 auto ast = parser_->Parse(input, options);
149 ast.Binder()->SetProgram(&ast);
150
151 if (options.dumpAst) {
152 std::cout << ast.Dump() << std::endl;
153 }
154
155 if (ast.Extension() == ScriptExtension::TS && options.enableTypeCheck) {
156 ArenaAllocator localAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
157 auto checker = std::make_unique<checker::Checker>(&localAllocator, ast.Binder());
158 checker->StartChecker();
159 }
160
161 if (ast.Extension() == ScriptExtension::TS) {
162 transformer_->Transform(&ast);
163 ast.Binder()->IdentifierAnalysis(binder::ResolveBindingFlags::TS_AFTER_TRANSFORM);
164 if (options.dumpTransformedAst) {
165 std::cout << ast.Dump() << std::endl;
166 }
167 if (options.checkTransformedAstStructure) {
168 transformer_->CheckTransformedAstStructure(&ast);
169 }
170 }
171
172 if (options.parseOnly) {
173 return nullptr;
174 }
175
176 std::string debugInfoSourceFile = options.debugInfoSourceFile.empty() ?
177 sourcefile : options.debugInfoSourceFile;
178 auto *prog = compiler_->Compile(&ast, options, debugInfoSourceFile, pkgName);
179
180 CleanPatchFixHelper(patchFixHelper);
181 return prog;
182 } catch (const class Error &e) {
183 error_ = e;
184
185 CleanPatchFixHelper(patchFixHelper);
186 return nullptr;
187 }
188 }
189
InitPatchFixHelper(const SourceFile & input,const CompilerOptions & options,util::SymbolTable * symbolTable)190 util::PatchFix *Compiler::InitPatchFixHelper(const SourceFile &input, const CompilerOptions &options,
191 util::SymbolTable *symbolTable)
192 {
193 util::PatchFix *patchFixHelper = nullptr;
194 bool needDumpSymbolFile = !options.patchFixOptions.dumpSymbolTable.empty();
195 bool needGeneratePatch = options.patchFixOptions.generatePatch && !options.patchFixOptions.symbolTable.empty();
196 bool isHotReload = options.patchFixOptions.hotReload;
197 bool isColdReload = options.patchFixOptions.coldReload;
198 bool isColdFix = options.patchFixOptions.coldFix;
199 if (symbolTable && (needDumpSymbolFile || needGeneratePatch || isHotReload || isColdReload)) {
200 util::PatchFixKind patchFixKind = util::PatchFixKind::DUMPSYMBOLTABLE;
201 if (needGeneratePatch) {
202 patchFixKind = isColdFix ? util::PatchFixKind::COLDFIX : util::PatchFixKind::HOTFIX;
203 }
204 if (isHotReload) {
205 patchFixKind = util::PatchFixKind::HOTRELOAD;
206 }
207 if (isColdReload) {
208 patchFixKind = util::PatchFixKind::COLDRELOAD;
209 }
210 patchFixHelper = new util::PatchFix(needDumpSymbolFile, needGeneratePatch, patchFixKind, input.recordName,
211 symbolTable);
212 parser_->AddPatchFixHelper(patchFixHelper);
213 compiler_->AddPatchFixHelper(patchFixHelper);
214 }
215 return patchFixHelper;
216 }
217
CleanPatchFixHelper(const util::PatchFix * patchFixHelper)218 void Compiler::CleanPatchFixHelper(const util::PatchFix *patchFixHelper)
219 {
220 if (patchFixHelper) {
221 delete patchFixHelper;
222 patchFixHelper = nullptr;
223 }
224 }
225
DumpAsm(const panda::pandasm::Program * prog)226 void Compiler::DumpAsm(const panda::pandasm::Program *prog)
227 {
228 compiler::CompilerImpl::DumpAsm(prog);
229 }
230
CompileFiles(CompilerOptions & options,std::map<std::string,panda::es2panda::util::ProgramCache * > & progsInfo,panda::ArenaAllocator * allocator)231 int Compiler::CompileFiles(CompilerOptions &options,
232 std::map<std::string, panda::es2panda::util::ProgramCache*> &progsInfo, panda::ArenaAllocator *allocator)
233 {
234 util::SymbolTable *symbolTable = nullptr;
235 if (!options.patchFixOptions.symbolTable.empty() || !options.patchFixOptions.dumpSymbolTable.empty()) {
236 symbolTable = new util::SymbolTable(options.patchFixOptions.symbolTable,
237 options.patchFixOptions.dumpSymbolTable);
238 if (!symbolTable->Initialize(options.targetApiVersion, options.targetApiSubVersion)) {
239 std::cerr << "Failed to initialize for Hotfix." << std::endl;
240 return 1;
241 }
242 }
243
244 bool failed = false;
245 std::unordered_set<std::string> optimizationPendingProgs;
246 auto queue = new compiler::CompileFileQueue(options.fileThreadCount, &options, progsInfo,
247 optimizationPendingProgs, symbolTable, allocator);
248
249 try {
250 queue->Schedule();
251 queue->Consume();
252 queue->Wait();
253 } catch (const class Error &e) {
254 if (!e.Reported()) {
255 std::cerr << e.TypeString() << ": " << e.Message() << std::endl;
256 }
257 failed = true;
258 }
259
260 delete queue;
261 queue = nullptr;
262
263 if (symbolTable) {
264 if (!options.patchFixOptions.dumpSymbolTable.empty()) {
265 symbolTable->WriteSymbolTable();
266 }
267 delete symbolTable;
268 symbolTable = nullptr;
269 }
270
271 if (options.requireGlobalOptimization) {
272 auto postAnalysisOptimizeQueue = new compiler::PostAnalysisOptimizeFileQueue(options.fileThreadCount,
273 progsInfo,
274 optimizationPendingProgs);
275 try {
276 postAnalysisOptimizeQueue->Schedule();
277 postAnalysisOptimizeQueue->Consume();
278 postAnalysisOptimizeQueue->Wait();
279 } catch (const class Error &e) {
280 // Optimization failed, but the program can still be used as unoptimized
281 }
282 delete postAnalysisOptimizeQueue;
283 }
284
285 return failed ? 1 : 0;
286 }
287
CompileFile(const CompilerOptions & options,SourceFile * src,util::SymbolTable * symbolTable)288 panda::pandasm::Program *Compiler::CompileFile(const CompilerOptions &options, SourceFile *src,
289 util::SymbolTable *symbolTable)
290 {
291 auto *program = Compile(*src, options, symbolTable);
292 if (!program) {
293 auto &err = GetError();
294 if (err.Message().empty() && options.parseOnly) {
295 return nullptr;
296 }
297
298 std::cerr << err.TypeString() << ": " << err.Message();
299 std::cerr << " [" << util::Helpers::BaseName(src->fileName) << ":"
300 << err.Line() << ":" << err.Col() << "]" << std::endl;
301 err.SetReported(true);
302
303 throw err;
304 }
305 return program;
306 }
307
308 } // namespace panda::es2panda
309