• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &regSpiller, 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