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