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