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
48 namespace ark::es2panda::compiler {
49
HandleContextLiterals(public_lib::Context * context)50 void 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
Emit(public_lib::Context * context)62 ark::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
76 class ASTVerificationRunner {
77 public:
78 class Result {
79 public:
Result(JsonArrayBuilder && warnings,JsonArrayBuilder && errors)80 explicit Result(JsonArrayBuilder &&warnings, JsonArrayBuilder &&errors)
81 : warnings_ {std::move(warnings)}, errors_ {std::move(errors)}
82 {
83 }
84
Warnings()85 JsonArrayBuilder &&Warnings()
86 {
87 return std::move(warnings_);
88 }
89
Errors()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
ASTVerificationRunner(ArenaAllocator & allocator,const public_lib::Context & context)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
Verify(const AstToCheck & astToCheck,const PhaseName & phaseName,const ast_verifier::InvariantNameSet & accumulatedChecks)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
DumpMessages()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
ExtractAst(const parser::Program & p)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
167 private:
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
175 template <typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter, typename AstCompiler>
MakeCompileJob()176 static 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
RunVerifierAndPhases(CompilerImpl * compilerImpl,public_lib::Context & context,const std::vector<Phase * > & phases,parser::Program & program)191 static 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
RunPhases(CompilerImpl * compilerImpl,public_lib::Context & context,const std::vector<Phase * > & phases,parser::Program & program)230 static 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
CreateDebuggerEvaluationPlugin(checker::ETSChecker & checker,ArenaAllocator & allocator,parser::Program * program,const CompilerOptions & options)243 static 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
254 using EmitCb = std::function<pandasm::Program *(public_lib::Context *)>;
255 using PhaseListGetter = std::function<std::vector<compiler::Phase *>(ScriptExtension)>;
256
ParserErrorChecker(bool isAnyError,parser::Program * program,CompilerImpl * compilerImpl,const CompilationUnit & unit)257 static 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
270 template <typename Parser, typename VarBinder, typename Checker, typename Analyzer, typename AstCompiler,
271 typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter>
CreateCompiler(const CompilationUnit & unit,const PhaseListGetter & getPhases,CompilerImpl * compilerImpl)272 static 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
Compile(const CompilationUnit & unit)334 pandasm::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
DumpAsm(const ark::pandasm::Program * prog)365 void CompilerImpl::DumpAsm(const ark::pandasm::Program *prog)
366 {
367 Emitter::DumpAsm(prog);
368 }
369
GetPhasesList(const ScriptExtension ext)370 std::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