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