1 /*
2 * Copyright (c) 2021-2022 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 "compileQueue.h"
17
18 #include <binder/binder.h>
19 #include <binder/scope.h>
20 #include <compiler/core/compilerContext.h>
21 #include <compiler/core/emitter/emitter.h>
22 #include <compiler/core/function.h>
23 #include <compiler/core/pandagen.h>
24 #include <es2panda.h>
25 #include <mem/arena_allocator.h>
26 #include <mem/pool_manager.h>
27 #include <protobufSnapshotGenerator.h>
28 #include <util/commonUtil.h>
29 #include <util/dumper.h>
30 #include <util/helpers.h>
31
32 namespace panda::es2panda::compiler {
33
34 std::mutex CompileFileJob::globalMutex_;
35 std::mutex CompileAbcClassQueue::globalMutex_;
36
Run()37 void CompileFunctionJob::Run()
38 {
39 std::unique_lock<std::mutex> lock(m_);
40 cond_.wait(lock, [this] { return dependencies_ == 0; });
41
42 ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
43 PandaGen pg(&allocator, context_, scope_);
44
45 Function::Compile(&pg);
46
47 FunctionEmitter funcEmitter(&allocator, &pg);
48 funcEmitter.Generate(context_->PatchFixHelper());
49
50 context_->GetEmitter()->AddFunction(&funcEmitter, context_);
51
52 for (auto *dependant : dependants_) {
53 dependant->Signal();
54 }
55 }
56
Run()57 void CompileModuleRecordJob::Run()
58 {
59 std::unique_lock<std::mutex> lock(m_);
60 cond_.wait(lock, [this] { return dependencies_ == 0; });
61
62 bool hasLazyImport = context_->Binder()->Program()->ModuleRecord()->HasLazyImport();
63 ModuleRecordEmitter moduleEmitter(context_->Binder()->Program()->ModuleRecord(), context_->NewLiteralIndex(),
64 hasLazyImport ? context_->NewLiteralIndex() : -1);
65 moduleEmitter.Generate();
66
67 context_->GetEmitter()->AddSourceTextModuleRecord(&moduleEmitter, context_);
68
69 for (auto *dependant : dependants_) {
70 dependant->Signal();
71 }
72 }
73
RetrieveProgramFromCacheFiles(const std::string & buffer)74 bool CompileFileJob::RetrieveProgramFromCacheFiles(const std::string &buffer)
75 {
76 if (options_->requireGlobalOptimization) {
77 return false;
78 }
79 auto cacheFileIter = options_->cacheFiles.find(src_->fileName);
80 // Disable the use of file caching when cross-program optimization is required, to prevent cached files from
81 // not being invalidated when their dependencies change, or from not being reanalyzed when their dependents
82 // are updated
83 if (cacheFileIter != options_->cacheFiles.end()) {
84 // cache is invalid when any one of source file infos being changed
85 auto bufToHash = buffer + src_->fileName + src_->recordName + src_->sourcefile + src_->pkgName;
86 src_->hash = GetHash32String(reinterpret_cast<const uint8_t *>(bufToHash.c_str()));
87
88 ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
89 auto *cacheProgramInfo = proto::ProtobufSnapshotGenerator::GetCacheContext(cacheFileIter->second,
90 &allocator);
91
92 if (cacheProgramInfo != nullptr && cacheProgramInfo->hashCode == src_->hash) {
93 std::unique_lock<std::mutex> lock(globalMutex_);
94 auto *cache = allocator_->New<util::ProgramCache>(src_->hash, std::move(cacheProgramInfo->program));
95 progsInfo_.insert({src_->fileName, cache});
96 return true;
97 }
98 }
99 return false;
100 }
101
Run()102 void CompileFileJob::Run()
103 {
104 std::stringstream ss;
105 std::string buffer;
106 if (!src_->fileName.empty() && src_->isSourceMode) {
107 if (!util::Helpers::ReadFileToBuffer(src_->fileName, ss)) {
108 return;
109 }
110 buffer = ss.str();
111 src_->source = buffer;
112 if (RetrieveProgramFromCacheFiles(buffer)) {
113 return;
114 }
115 }
116
117 es2panda::Compiler compiler(src_->scriptExtension, options_->functionThreadCount);
118 panda::pandasm::Program *prog = nullptr;
119 if (src_->isSourceMode) {
120 prog = compiler.CompileFile(*options_, src_, symbolTable_);
121 } else if (!options_->mergeAbc) {
122 // If input is an abc file, in non merge-abc mode, compile classes one by one.
123 prog = compiler.CompileAbcFile(src_->fileName, *options_);
124 } else {
125 // If input is an abc file, in merge-abc mode, compile each class parallelly.
126 compiler.CompileAbcFileInParallel(src_, *options_, progsInfo_, allocator_);
127 return;
128 }
129 if (prog == nullptr) {
130 return;
131 }
132
133 bool requireOptimizationAfterAnalysis = false;
134 // When cross-program optimizations are required, skip program-local optimization at this stage
135 // and perform it later after the analysis of all programs has been completed
136 if (src_->isSourceMode && options_->transformLib.empty()) {
137 if (options_->requireGlobalOptimization) {
138 util::Helpers::AnalysisProgram(prog, src_->fileName);
139 requireOptimizationAfterAnalysis = true;
140 } else if (options_->optLevel != 0) {
141 util::Helpers::OptimizeProgram(prog, src_->fileName);
142 }
143 }
144
145 {
146 std::unique_lock<std::mutex> lock(globalMutex_);
147 auto *cache = allocator_->New<util::ProgramCache>(src_->hash, std::move(*prog), src_->isSourceMode);
148 progsInfo_.insert({src_->fileName, cache});
149 if (requireOptimizationAfterAnalysis) {
150 optimizationPendingProgs_.insert(src_->fileName);
151 }
152 }
153 }
154
Run()155 void CompileAbcClassJob::Run()
156 {
157 panda_file::File::EntityId recordId(classId_);
158 auto *program = new panda::pandasm::Program();
159 compiler_.CompileAbcClass(recordId, *program);
160 program->isGeneratedFromMergedAbc = true;
161
162 // Update ohmurl for abc input when needed
163 if (options_.compileContextInfo.needModifyRecord ||
164 (options_.updatePkgVersionForAbcInput && pkgVersionUpdateRequiredInAbc_)) {
165 UpdateImportOhmurl(program, options_);
166 // Remove redundant strings created due to version replacement
167 if (options_.removeRedundantFile && hasOhmurlBeenChanged_) {
168 program->strings.clear();
169 for (const auto &[_, function] : program->function_table) {
170 const auto &funcStringSet = function.CollectStringsFromFunctionInsns();
171 program->strings.insert(funcStringSet.begin(), funcStringSet.end());
172 }
173 }
174 }
175
176 {
177 std::unique_lock<std::mutex> lock(CompileFileJob::globalMutex_);
178 ASSERT(compiler_.GetAbcFile().GetFilename().find(util::CHAR_VERTICAL_LINE) == std::string::npos);
179 ASSERT(program->record_table.size() == 1);
180 ASSERT(util::RecordNotGeneratedFromBytecode(program->record_table.begin()->first));
181 auto name = compiler_.GetAbcFile().GetFilename();
182 name += util::CHAR_VERTICAL_LINE + program->record_table.begin()->first;
183 auto *cache = allocator_->New<util::ProgramCache>(std::move(*program));
184 progsInfo_.emplace(name, cache);
185 }
186
187 delete program;
188 program = nullptr;
189 }
190
UpdateBundleNameOfOhmurl(std::string & ohmurl)191 void CompileAbcClassJob::UpdateBundleNameOfOhmurl(std::string &ohmurl)
192 {
193 const auto &newOhmurl = util::UpdateBundleNameIfNeeded(ohmurl, options_.compileContextInfo.bundleName,
194 options_.compileContextInfo.externalPkgNames);
195 if (newOhmurl == ohmurl) {
196 return;
197 }
198 SetOhmurlBeenChanged(true);
199 ohmurl = newOhmurl;
200 }
201
UpdateDynamicImport(panda::pandasm::Program * prog,const std::unordered_map<std::string,panda::es2panda::PkgInfo> & pkgContextInfo)202 void CompileAbcClassJob::UpdateDynamicImport(panda::pandasm::Program *prog,
203 const std::unordered_map<std::string, panda::es2panda::PkgInfo> &pkgContextInfo)
204 {
205 for (auto &[name, function] : prog->function_table) {
206 util::VisitDyanmicImports<false>(function, [this, &prog, pkgContextInfo](std::string &ohmurl) {
207 if (this->options_.compileContextInfo.needModifyRecord) {
208 this->UpdateBundleNameOfOhmurl(ohmurl);
209 }
210 const auto &newOhmurl = util::UpdatePackageVersionIfNeeded(ohmurl, pkgContextInfo);
211 if (newOhmurl == ohmurl) {
212 return;
213 }
214 this->SetOhmurlBeenChanged(true);
215 prog->strings.insert(newOhmurl);
216 ohmurl = newOhmurl;
217 });
218 }
219 }
220
UpdateStaticImport(panda::pandasm::Program * prog,const std::unordered_map<std::string,panda::es2panda::PkgInfo> & pkgContextInfo)221 void CompileAbcClassJob::UpdateStaticImport(panda::pandasm::Program *prog,
222 const std::unordered_map<std::string, panda::es2panda::PkgInfo> &pkgContextInfo)
223 {
224 for (auto &[recordName, record] : prog->record_table) {
225 util::VisitStaticImports<false>(*prog, record, [this, pkgContextInfo](std::string &ohmurl) {
226 if (this->options_.compileContextInfo.needModifyRecord) {
227 this->UpdateBundleNameOfOhmurl(ohmurl);
228 }
229
230 const auto &newOhmurl = util::UpdatePackageVersionIfNeeded(ohmurl, pkgContextInfo);
231 if (newOhmurl == ohmurl) {
232 return;
233 }
234 this->SetOhmurlBeenChanged(true);
235 ohmurl = newOhmurl;
236 });
237 }
238 }
239
UpdateImportOhmurl(panda::pandasm::Program * prog,const panda::es2panda::CompilerOptions & options)240 void CompileAbcClassJob::UpdateImportOhmurl(panda::pandasm::Program *prog,
241 const panda::es2panda::CompilerOptions &options)
242 {
243 bool isAccurateUpdateVersion = !options.compileContextInfo.updateVersionInfo.empty();
244 const std::unordered_map<std::string, panda::es2panda::PkgInfo> &pkgContextInfo = isAccurateUpdateVersion ?
245 options.compileContextInfo.updateVersionInfo.at(abcPkgName_) : options.compileContextInfo.pkgContextInfo;
246 // Replace for esm module static import
247 UpdateStaticImport(prog, pkgContextInfo);
248 // Replace for dynamic import
249 UpdateDynamicImport(prog, pkgContextInfo);
250 }
251
Run()252 void PostAnalysisOptimizeFileJob::Run()
253 {
254 util::Helpers::OptimizeProgram(program_, fileName_);
255 }
256
Schedule()257 void CompileFuncQueue::Schedule()
258 {
259 ASSERT(jobsCount_ == 0);
260 std::unique_lock<std::mutex> lock(m_);
261 const auto &functions = context_->Binder()->Functions();
262
263 for (auto *function : functions) {
264 auto *funcJob = new CompileFunctionJob(context_);
265 funcJob->SetFunctionScope(function);
266 jobs_.push_back(funcJob);
267 jobsCount_++;
268 }
269
270 if (context_->Binder()->Program()->Kind() == parser::ScriptKind::MODULE) {
271 auto *moduleRecordJob = new CompileModuleRecordJob(context_);
272 jobs_.push_back(moduleRecordJob);
273 jobsCount_++;
274 }
275
276 lock.unlock();
277 jobsAvailable_.notify_all();
278 }
279
Schedule()280 void CompileFileQueue::Schedule()
281 {
282 ASSERT(jobsCount_ == 0);
283 std::unique_lock<std::mutex> lock(m_);
284
285 for (auto &input: options_->sourceFiles) {
286 auto *fileJob = new CompileFileJob(&input, options_, progsInfo_, optimizationPendingProgs_,
287 symbolTable_, allocator_);
288 jobs_.push_back(fileJob);
289 jobsCount_++;
290 }
291
292 lock.unlock();
293 jobsAvailable_.notify_all();
294 }
295
NeedUpdateVersion()296 bool CompileAbcClassQueue::NeedUpdateVersion()
297 {
298 std::unordered_map<std::string, std::unordered_map<std::string, panda::es2panda::PkgInfo>> updateVersionInfo =
299 options_.compileContextInfo.updateVersionInfo;
300 auto iter = updateVersionInfo.find(src_->pkgName);
301 return updateVersionInfo.empty() || (iter != updateVersionInfo.end() && !iter->second.empty());
302 }
303
Schedule()304 void CompileAbcClassQueue::Schedule()
305 {
306 std::unique_lock<std::mutex> lock(m_);
307
308 auto classIds = compiler_.GetAbcFile().GetClasses();
309 size_t expectedProgsCountInAbcFile = 0;
310 bool needUpdateVersion = NeedUpdateVersion();
311 for (size_t i = 0; i != classIds.size(); ++i) {
312 if (!compiler_.CheckClassId(classIds[i], i)) {
313 continue;
314 }
315
316 auto *abcClassJob = new CompileAbcClassJob(classIds[i], options_, compiler_, progsInfo_, allocator_,
317 src_->pkgName, needUpdateVersion);
318
319 jobs_.push_back(abcClassJob);
320 jobsCount_++;
321 expectedProgsCountInAbcFile++;
322 }
323 {
324 std::unique_lock<std::mutex> lock(globalMutex_);
325 Compiler::SetExpectedProgsCount(Compiler::GetExpectedProgsCount() + expectedProgsCountInAbcFile - 1);
326 }
327
328 lock.unlock();
329 jobsAvailable_.notify_all();
330 }
331
Schedule()332 void PostAnalysisOptimizeFileQueue::Schedule()
333 {
334 ASSERT(jobsCount_ == 0);
335 std::unique_lock<std::mutex> lock(m_);
336
337 for (const auto &optimizationPendingProgName : optimizationPendingProgs_) {
338 auto progInfo = progsInfo_.find(optimizationPendingProgName);
339 if (progInfo == progsInfo_.end()) {
340 continue;
341 }
342 auto *optimizeJob = new PostAnalysisOptimizeFileJob(progInfo->first, &progInfo->second->program);
343 jobs_.push_back(optimizeJob);
344 jobsCount_++;
345 }
346 }
347
348 } // namespace panda::es2panda::compiler
349