1/** 2 * Copyright (c) 2021-2024 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 "compilerImpl.h" 17 18#include "ast_verifier/ASTVerifier.h" 19#include "es2panda.h" 20#include "checker/ETSAnalyzer.h" 21#include "checker/TSAnalyzer.h" 22#include "compiler/core/compileQueue.h" 23#include "compiler/core/compilerImpl.h" 24#include "compiler/core/pandagen.h" 25#include "compiler/core/ETSCompiler.h" 26#include "compiler/core/ETSGen.h" 27#include "compiler/core/JSCompiler.h" 28#include "compiler/core/JSemitter.h" 29#include "compiler/core/ETSemitter.h" 30#include "compiler/lowering/phase.h" 31#include "evaluate/scopedDebugInfoPlugin.h" 32#include "parser/parserImpl.h" 33#include "parser/JSparser.h" 34#include "parser/ASparser.h" 35#include "parser/TSparser.h" 36#include "parser/ETSparser.h" 37#include "parser/program/program.h" 38#include "varbinder/JSBinder.h" 39#include "varbinder/ASBinder.h" 40#include "varbinder/TSBinder.h" 41#include "varbinder/ETSBinder.h" 42#include "checker/TSchecker.h" 43#include "checker/ETSchecker.h" 44#include "checker/ASchecker.h" 45#include "checker/JSchecker.h" 46#include "public/public.h" 47 48namespace ark::es2panda::compiler { 49 50void CompilerImpl::HandleContextLiterals(public_lib::Context *context) 51{ 52 auto *emitter = context->emitter; 53 54 uint32_t index = 0; 55 for (const auto &buff : context->contextLiterals) { 56 emitter->AddLiteralBuffer(buff, index++); 57 } 58 59 emitter->LiteralBufferIndex() += context->contextLiterals.size(); 60} 61 62ark::pandasm::Program *CompilerImpl::Emit(public_lib::Context *context) 63{ 64 HandleContextLiterals(context); 65 66 queue_.Schedule(context); 67 68 /* Main thread can also be used instead of idling */ 69 queue_.Consume(); 70 auto *emitter = context->emitter; 71 queue_.Wait([emitter](CompileJob *job) { emitter->AddProgramElement(job->GetProgramElement()); }); 72 73 return emitter->Finalize(context->config->options->CompilerOptions().dumpDebugInfo, Signatures::ETS_GLOBAL); 74} 75 76class ASTVerificationRunner { 77public: 78 class Result { 79 public: 80 explicit Result(JsonArrayBuilder &&warnings, JsonArrayBuilder &&errors) 81 : warnings_ {std::move(warnings)}, errors_ {std::move(errors)} 82 { 83 } 84 85 JsonArrayBuilder &&Warnings() 86 { 87 return std::move(warnings_); 88 } 89 90 JsonArrayBuilder &&Errors() 91 { 92 return std::move(errors_); 93 } 94 95 private: 96 JsonArrayBuilder warnings_; 97 JsonArrayBuilder errors_; 98 }; 99 100 using AstPath = std::string; 101 using PhaseName = std::string; 102 using Source = std::tuple<AstPath, PhaseName>; 103 using AstToCheck = ArenaMap<AstPath, const ir::AstNode *>; 104 using GroupedMessages = std::map<Source, ast_verifier::Messages>; 105 106 ASTVerificationRunner(ArenaAllocator &allocator, const public_lib::Context &context) 107 : checkFullProgram_ {context.config->options->CompilerOptions().verifierFullProgram}, 108 verifier_ {&allocator}, 109 treatAsWarnings_ {context.config->options->CompilerOptions().verifierWarnings}, 110 treatAsErrors_ {context.config->options->CompilerOptions().verifierErrors} 111 { 112 } 113 114 void Verify(const AstToCheck &astToCheck, const PhaseName &phaseName, 115 const ast_verifier::InvariantNameSet &accumulatedChecks) 116 { 117 for (const auto &[sourceName, ast] : astToCheck) { 118 const auto source = Source(sourceName, phaseName); 119 auto messages = verifier_.Verify(ast, accumulatedChecks); 120 auto &sourcedReport = report_[source]; 121 std::copy(messages.begin(), messages.end(), std::back_inserter(sourcedReport)); 122 } 123 } 124 125 Result DumpMessages() 126 { 127 auto warnings = JsonArrayBuilder {}; 128 auto errors = JsonArrayBuilder {}; 129 const auto filterMessages = [this, &warnings, &errors](const ast_verifier::CheckMessage &message, 130 const std::string &sourceName, 131 const std::string &phaseName) { 132 auto invariant = message.Invariant(); 133 if (auto found = treatAsWarnings_.find(invariant); found != treatAsWarnings_.end()) { 134 warnings.Add(message.DumpJSON(ast_verifier::CheckSeverity::WARNING, sourceName, phaseName)); 135 return; 136 } 137 if (auto found = treatAsErrors_.find(invariant); found != treatAsErrors_.end()) { 138 errors.Add(message.DumpJSON(ast_verifier::CheckSeverity::ERROR, sourceName, phaseName)); 139 } 140 }; 141 142 for (const auto &[source, messages] : report_) { 143 const auto &[sourceName, phaseName] = source; 144 for (const auto &message : messages) { 145 filterMessages(message, sourceName, phaseName); 146 } 147 } 148 149 return Result {std::move(warnings), std::move(errors)}; 150 } 151 152 ASTVerificationRunner::AstToCheck ExtractAst(const parser::Program &p) 153 { 154 auto &allocator = *p.Allocator(); 155 auto astToCheck = ASTVerificationRunner::AstToCheck {allocator.Adapter()}; 156 astToCheck.insert(std::make_pair(p.SourceFilePath(), p.Ast())); 157 if (checkFullProgram_) { 158 for (const auto &externalSource : p.ExternalSources()) { 159 for (auto *external : externalSource.second) { 160 astToCheck.insert(std::make_pair(external->SourceFilePath(), external->Ast())); 161 } 162 } 163 } 164 return astToCheck; 165 } 166 167private: 168 bool checkFullProgram_; 169 GroupedMessages report_; 170 ast_verifier::ASTVerifier verifier_; 171 std::unordered_set<std::string> treatAsWarnings_; 172 std::unordered_set<std::string> treatAsErrors_; 173}; 174 175template <typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter, typename AstCompiler> 176static public_lib::Context::CodeGenCb MakeCompileJob() 177{ 178 return [](public_lib::Context *context, varbinder::FunctionScope *scope, 179 compiler::ProgramElement *programElement) -> void { 180 RegSpiller regSpiller; 181 ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); 182 AstCompiler astcompiler; 183 CodeGen cg(&allocator, ®Spiller, context, std::make_tuple(scope, programElement, &astcompiler)); 184 FunctionEmitter funcEmitter(&cg, programElement); 185 funcEmitter.Generate(); 186 }; 187} 188 189#ifndef NDEBUG 190 191static bool RunVerifierAndPhases(CompilerImpl *compilerImpl, public_lib::Context &context, 192 const std::vector<Phase *> &phases, parser::Program &program) 193{ 194 auto runner = ASTVerificationRunner(*context.allocator, context); 195 auto verificationCtx = ast_verifier::VerificationContext {}; 196 const auto runAllChecks = context.config->options->CompilerOptions().verifierAllChecks; 197 198 for (auto *phase : phases) { 199 if (!phase->Apply(&context, &program)) { 200 compilerImpl->SetIsAnyError(context.checker->ErrorLogger()->IsAnyError() || 201 context.parser->ErrorLogger()->IsAnyError()); 202 return false; 203 } 204 205 if (runAllChecks) { 206 auto ast = runner.ExtractAst(program); 207 runner.Verify(ast, std::string {phase->Name()}, verificationCtx.AccumulatedChecks()); 208 } 209 verificationCtx.IntroduceNewInvariants(phase->Name()); 210 } 211 212 if (!runAllChecks) { 213 auto ast = runner.ExtractAst(program); 214 runner.Verify(ast, "AfterAllPhases", verificationCtx.AccumulatedChecks()); 215 } 216 217 auto result = runner.DumpMessages(); 218 if (auto warnings = result.Warnings().Build(); warnings != "[]") { 219 LOG(WARNING, ES2PANDA) << warnings; 220 } 221 222 if (auto errors = result.Errors().Build(); errors != "[]") { 223 ASSERT_PRINT(false, errors); 224 } 225 226 return true; 227} 228#endif 229 230static bool RunPhases(CompilerImpl *compilerImpl, public_lib::Context &context, const std::vector<Phase *> &phases, 231 parser::Program &program) 232{ 233 for (auto *phase : phases) { 234 if (!phase->Apply(&context, &program)) { 235 compilerImpl->SetIsAnyError(context.checker->ErrorLogger()->IsAnyError() || 236 context.parser->ErrorLogger()->IsAnyError()); 237 return false; 238 } 239 } 240 return true; 241} 242 243static void CreateDebuggerEvaluationPlugin(checker::ETSChecker &checker, ArenaAllocator &allocator, 244 parser::Program *program, const CompilerOptions &options) 245{ 246 // Sometimes evaluation mode might work without project context. 247 // In this case, users might omit context files. 248 if (options.debuggerEvalMode && !options.debuggerEvalPandaFiles.empty()) { 249 auto *plugin = allocator.New<evaluate::ScopedDebugInfoPlugin>(program, &checker, options); 250 checker.SetDebugInfoPlugin(plugin); 251 } 252} 253 254using EmitCb = std::function<pandasm::Program *(public_lib::Context *)>; 255using PhaseListGetter = std::function<std::vector<compiler::Phase *>(ScriptExtension)>; 256 257static bool ParserErrorChecker(bool isAnyError, parser::Program *program, CompilerImpl *compilerImpl, 258 const CompilationUnit &unit) 259{ 260 if (isAnyError) { 261 compilerImpl->SetIsAnyError(isAnyError); 262 if (unit.options.CompilerOptions().dumpAst) { 263 std::cout << program->Dump() << std::endl; 264 } 265 return false; 266 } 267 return true; 268} 269 270template <typename Parser, typename VarBinder, typename Checker, typename Analyzer, typename AstCompiler, 271 typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter> 272static pandasm::Program *CreateCompiler(const CompilationUnit &unit, const PhaseListGetter &getPhases, 273 CompilerImpl *compilerImpl) 274{ 275 ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); 276 auto program = parser::Program::NewProgram<VarBinder>(&allocator); 277 program.MarkEntry(); 278 auto parser = 279 Parser(&program, unit.options.CompilerOptions(), static_cast<parser::ParserStatus>(unit.rawParserStatus)); 280 auto checker = Checker(); 281 auto analyzer = Analyzer(&checker); 282 checker.SetAnalyzer(&analyzer); 283 284 auto *varbinder = program.VarBinder(); 285 varbinder->SetProgram(&program); 286 287 if constexpr (std::is_same_v<Checker, checker::ETSChecker>) { 288 CreateDebuggerEvaluationPlugin(checker, allocator, &program, unit.options.CompilerOptions()); 289 } 290 291 public_lib::Context context; 292 293 auto config = public_lib::ConfigImpl {}; 294 context.config = &config; 295 context.config->options = &unit.options; 296 context.sourceFile = &unit.input; 297 context.allocator = &allocator; 298 context.queue = compilerImpl->Queue(); 299 context.plugins = &compilerImpl->Plugins(); 300 context.parser = &parser; 301 context.checker = &checker; 302 context.analyzer = checker.GetAnalyzer(); 303 context.parserProgram = &program; 304 context.codeGenCb = MakeCompileJob<CodeGen, RegSpiller, FunctionEmitter, Emitter, AstCompiler>(); 305 306 auto emitter = Emitter(&context); 307 context.emitter = &emitter; 308 309 varbinder->SetContext(&context); 310 311 parser.ParseScript(unit.input, unit.options.CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB); 312 if (!ParserErrorChecker(parser.ErrorLogger()->IsAnyError(), &program, compilerImpl, unit)) { 313 return nullptr; 314 } 315#ifndef NDEBUG 316 if (unit.ext == ScriptExtension::ETS) { 317 if (!RunVerifierAndPhases(compilerImpl, context, getPhases(unit.ext), program)) { 318 return nullptr; 319 } 320 } else if (!RunPhases(compilerImpl, context, getPhases(unit.ext), program)) { 321 return nullptr; 322 } 323#else 324 if (!RunPhases(compilerImpl, context, getPhases(unit.ext), program)) { 325 return nullptr; 326 } 327#endif 328 329 emitter.GenAnnotation(); 330 331 return compilerImpl->Emit(&context); 332} 333 334pandasm::Program *CompilerImpl::Compile(const CompilationUnit &unit) 335{ 336 switch (unit.ext) { 337 case ScriptExtension::TS: { 338 return CreateCompiler<parser::TSParser, varbinder::TSBinder, checker::TSChecker, checker::TSAnalyzer, 339 compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller, 340 compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, compiler::GetPhaseList, this); 341 } 342 case ScriptExtension::AS: { 343 return CreateCompiler<parser::ASParser, varbinder::ASBinder, checker::ASChecker, checker::TSAnalyzer, 344 compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller, 345 compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, compiler::GetPhaseList, this); 346 } 347 case ScriptExtension::ETS: { 348 return CreateCompiler<parser::ETSParser, varbinder::ETSBinder, checker::ETSChecker, checker::ETSAnalyzer, 349 compiler::ETSCompiler, compiler::ETSGen, compiler::StaticRegSpiller, 350 compiler::ETSFunctionEmitter, compiler::ETSEmitter>(unit, compiler::GetPhaseList, 351 this); 352 } 353 case ScriptExtension::JS: { 354 return CreateCompiler<parser::JSParser, varbinder::JSBinder, checker::JSChecker, checker::TSAnalyzer, 355 compiler::JSCompiler, compiler::PandaGen, compiler::DynamicRegSpiller, 356 compiler::JSFunctionEmitter, compiler::JSEmitter>(unit, compiler::GetPhaseList, this); 357 } 358 default: { 359 UNREACHABLE(); 360 return nullptr; 361 } 362 } 363} 364 365void CompilerImpl::DumpAsm(const ark::pandasm::Program *prog) 366{ 367 Emitter::DumpAsm(prog); 368} 369 370std::string CompilerImpl::GetPhasesList(const ScriptExtension ext) 371{ 372 std::stringstream ss; 373 for (auto phase : compiler::GetPhaseList(ext)) { 374 ss << " " << phase->Name() << std::endl; 375 } 376 return ss.str(); 377} 378 379} // namespace ark::es2panda::compiler 380