• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "ecmascript/compiler/bytecode_info_collector.h"
17 
18 #include "ecmascript/compiler/type_recorder.h"
19 #include "ecmascript/interpreter/interpreter-inl.h"
20 #include "ecmascript/jspandafile/type_literal_extractor.h"
21 #include "ecmascript/module/module_path_helper.h"
22 #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
23 #include "ecmascript/ts_types/ts_type_parser.h"
24 #include "libpandafile/code_data_accessor.h"
25 
26 namespace panda::ecmascript::kungfu {
27 template<class T, class... Args>
InitializeMemory(T * mem,Args...args)28 static T *InitializeMemory(T *mem, Args... args)
29 {
30     return new (mem) T(std::forward<Args>(args)...);
31 }
32 
BytecodeInfoCollector(EcmaVM * vm,JSPandaFile * jsPandaFile,PGOProfilerDecoder & pfDecoder,size_t maxAotMethodSize,bool enableCollectLiteralInfo)33 BytecodeInfoCollector::BytecodeInfoCollector(EcmaVM *vm, JSPandaFile *jsPandaFile, PGOProfilerDecoder &pfDecoder,
34                                              size_t maxAotMethodSize, bool enableCollectLiteralInfo)
35     : vm_(vm),
36       jsPandaFile_(jsPandaFile),
37       bytecodeInfo_(maxAotMethodSize),
38       pfDecoder_(pfDecoder),
39       snapshotCPData_(vm, jsPandaFile, &pfDecoder),
40       enableCollectLiteralInfo_(enableCollectLiteralInfo)
41 {
42     vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->SetBytecodeInfoCollector(this);
43     ProcessClasses();
44     ProcessEnvs();
45 }
46 
BytecodeInfoCollector(EcmaVM * vm,JSPandaFile * jsPandaFile,JSHandle<JSFunction> & jsFunction,PGOProfilerDecoder & pfDecoder,bool enableCollectLiteralInfo)47 BytecodeInfoCollector::BytecodeInfoCollector(EcmaVM *vm, JSPandaFile *jsPandaFile, JSHandle<JSFunction> &jsFunction,
48                                              PGOProfilerDecoder &pfDecoder, bool enableCollectLiteralInfo)
49     : vm_(vm),
50       jsPandaFile_(jsPandaFile),
51       bytecodeInfo_(1),
52       pfDecoder_(pfDecoder),
53       snapshotCPData_(vm, jsPandaFile, &pfDecoder),
54       enableCollectLiteralInfo_(enableCollectLiteralInfo)
55 {
56     vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->SetBytecodeInfoCollector(this);
57     ProcessMethod(jsFunction);
58     ProcessEnvs();
59 }
60 
~BytecodeInfoCollector()61 BytecodeInfoCollector::~BytecodeInfoCollector()
62 {
63     if (envManager_ != nullptr) {
64         delete envManager_;
65         envManager_ = nullptr;
66     }
67     auto tsManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager();
68     tsManager->PrintTypeInfo(jsPandaFile_);
69     tsManager->SetBytecodeInfoCollector(nullptr);
70 }
71 
ProcessEnvs()72 void BytecodeInfoCollector::ProcessEnvs()
73 {
74     if (envManager_ == nullptr) {
75         envManager_ = new LexEnvManager(bytecodeInfo_);
76     }
77 }
78 
ProcessClasses()79 void BytecodeInfoCollector::ProcessClasses()
80 {
81     ASSERT(jsPandaFile_ != nullptr && jsPandaFile_->GetMethodLiterals() != nullptr);
82     MethodLiteral *methods = jsPandaFile_->GetMethodLiterals();
83     const panda_file::File *pf = jsPandaFile_->GetPandaFile();
84     size_t methodIdx = 0;
85     std::map<uint32_t, std::pair<size_t, uint32_t>> processedMethod;
86     Span<const uint32_t> classIndexes = jsPandaFile_->GetClasses();
87 
88     auto &recordNames = bytecodeInfo_.GetRecordNames();
89     auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
90     std::vector<panda_file::File::EntityId> methodIndexes;
91     std::vector<panda_file::File::EntityId> classConstructIndexes;
92     for (const uint32_t index : classIndexes) {
93         panda_file::File::EntityId classId(index);
94         if (jsPandaFile_->IsExternal(classId)) {
95             continue;
96         }
97         panda_file::ClassDataAccessor cda(*pf, classId);
98         CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
99         const CString recordName = JSPandaFile::ParseEntryPoint(desc);
100         cda.EnumerateMethods([this, methods, &methodIdx, pf, &processedMethod,
101             &recordNames, &methodPcInfos, &recordName,
102             &methodIndexes, &classConstructIndexes] (panda_file::MethodDataAccessor &mda) {
103             auto methodId = mda.GetMethodId();
104             methodIndexes.emplace_back(methodId);
105             CollectFunctionTypeId(methodId);
106 
107             // Generate all constpool
108             vm_->GetJSThread()->GetCurrentEcmaContext()->FindOrCreateConstPool(jsPandaFile_, methodId);
109 
110             auto methodOffset = methodId.GetOffset();
111             CString name = reinterpret_cast<const char *>(jsPandaFile_->GetStringData(mda.GetNameId()).data);
112             if (JSPandaFile::IsEntryOrPatch(name)) {
113                 jsPandaFile_->UpdateMainMethodIndex(methodOffset, recordName);
114                 recordNames.emplace_back(recordName);
115             }
116 
117             MethodLiteral *methodLiteral = methods + (methodIdx++);
118             InitializeMemory(methodLiteral, methodId);
119             methodLiteral->Initialize(jsPandaFile_);
120 
121             ASSERT(jsPandaFile_->IsNewVersion());
122             panda_file::IndexAccessor indexAccessor(*pf, methodId);
123             panda_file::FunctionKind funcKind = indexAccessor.GetFunctionKind();
124             FunctionKind kind = JSPandaFile::GetFunctionKind(funcKind);
125             methodLiteral->SetFunctionKind(kind);
126 
127             auto codeId = mda.GetCodeId();
128             ASSERT(codeId.has_value());
129             panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
130             uint32_t codeSize = codeDataAccessor.GetCodeSize();
131             const uint8_t *insns = codeDataAccessor.GetInstructions();
132             auto it = processedMethod.find(methodOffset);
133             if (it == processedMethod.end()) {
134                 std::vector<std::string> classNameVec;
135                 CollectMethodPcsFromBC(codeSize, insns, methodLiteral, classNameVec,
136                     recordName, methodOffset, classConstructIndexes);
137                 processedMethod[methodOffset] = std::make_pair(methodPcInfos.size() - 1, methodOffset);
138                 // collect className and literal offset for type infer
139                 if (EnableCollectLiteralInfo()) {
140                     CollectClassLiteralInfo(methodLiteral, classNameVec);
141                 }
142             }
143 
144             SetMethodPcInfoIndex(methodOffset, processedMethod[methodOffset]);
145             jsPandaFile_->SetMethodLiteralToMap(methodLiteral);
146             pfDecoder_.MatchAndMarkMethod(jsPandaFile_, recordName, name.c_str(), methodId);
147         });
148     }
149     // class Construct need to use new target, can not fastcall
150     for (auto index : classConstructIndexes) {
151         MethodLiteral *method = jsPandaFile_->GetMethodLiteralByIndex(index.GetOffset());
152         if (method != nullptr) {
153             method->SetFunctionKind(FunctionKind::CLASS_CONSTRUCTOR);
154             method->SetIsFastCall(false);
155             bytecodeInfo_.ModifyMethodOffsetToCanFastCall(index.GetOffset(), false);
156         }
157     }
158     // Collect import(infer-needed) and export relationship among all records.
159     CollectRecordReferenceREL();
160     RearrangeInnerMethods();
161     LOG_COMPILER(INFO) << "Total number of methods in file: "
162                        << jsPandaFile_->GetJSPandaFileDesc()
163                        << " is: "
164                        << methodIdx;
165 }
166 
ProcessMethod(JSHandle<JSFunction> & jsFunction)167 void BytecodeInfoCollector::ProcessMethod(JSHandle<JSFunction> &jsFunction)
168 {
169     auto &recordNames = bytecodeInfo_.GetRecordNames();
170     auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
171 
172     Method *method = Method::Cast(jsFunction->GetMethod().GetTaggedObject());
173     const panda_file::File *pf = jsPandaFile_->GetPandaFile();
174     panda_file::File::EntityId methodIdx = method->GetMethodId();
175     panda_file::MethodDataAccessor mda(*pf, methodIdx);
176     panda_file::File::EntityId classIdx = panda_file::MethodDataAccessor::GetClassId(*pf, methodIdx);
177     panda_file::ClassDataAccessor cda(*pf, classIdx);
178     CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
179     const CString recordName = JSPandaFile::ParseEntryPoint(desc);
180     recordNames.emplace_back(recordName);
181     auto methodId = mda.GetMethodId();
182     CollectFunctionTypeId(methodId);
183 
184     // Generate all constpool
185     [[maybe_unused]] JSTaggedValue constpool =
186         vm_->GetJSThread()->GetCurrentEcmaContext()->FindConstpool(jsPandaFile_, methodId);
187     ASSERT(!constpool.IsHole());
188 
189     auto methodOffset = methodId.GetOffset();
190     CString name = reinterpret_cast<const char *>(jsPandaFile_->GetStringData(mda.GetNameId()).data);
191     if (JSPandaFile::IsEntryOrPatch(name)) {
192     }
193 
194     MethodLiteral *methodLiteral = method->GetMethodLiteral();
195     ASSERT(jsPandaFile_->IsNewVersion());
196 
197     auto codeId = mda.GetCodeId();
198     ASSERT(codeId.has_value());
199     panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
200     uint32_t codeSize = codeDataAccessor.GetCodeSize();
201     const uint8_t *insns = codeDataAccessor.GetInstructions();
202 
203     std::map<uint32_t, std::pair<size_t, uint32_t>> processedMethod;
204     std::vector<panda_file::File::EntityId> classConstructIndexes;
205     std::vector<std::string> classNameVec;
206 
207     CollectMethodPcsFromBC(codeSize, insns, methodLiteral, classNameVec,
208         recordName, methodOffset, classConstructIndexes);
209     processedMethod[methodOffset] = std::make_pair(methodPcInfos.size() - 1, methodOffset);
210     // collect className and literal offset for type infer
211     if (EnableCollectLiteralInfo()) {
212         CollectClassLiteralInfo(methodLiteral, classNameVec);
213     }
214 
215     SetMethodPcInfoIndex(methodOffset, processedMethod[methodOffset]);
216     // class Construct need to use new target, can not fastcall
217     if (method->GetFunctionKind() == FunctionKind::CLASS_CONSTRUCTOR) {
218         methodLiteral->SetIsFastCall(false);
219         bytecodeInfo_.ModifyMethodOffsetToCanFastCall(methodIdx.GetOffset(), false);
220     }
221     // Collect import(infer-needed) and export relationship among all records.
222     CollectRecordReferenceREL();
223     RearrangeInnerMethods();
224 }
225 
CollectClassLiteralInfo(const MethodLiteral * method,const std::vector<std::string> & classNameVec)226 void BytecodeInfoCollector::CollectClassLiteralInfo(const MethodLiteral *method,
227                                                     const std::vector<std::string> &classNameVec)
228 {
229     std::vector<uint32_t> classOffsetVec;
230     IterateLiteral(method, classOffsetVec);
231 
232     if (classOffsetVec.size() == classNameVec.size()) {
233         for (uint32_t i = 0; i < classOffsetVec.size(); i++) {
234             vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->AddElementToClassNameMap(
235                 jsPandaFile_, classOffsetVec[i], classNameVec[i]);
236         }
237     }
238 }
239 
CollectFunctionTypeId(panda_file::File::EntityId fieldId)240 void BytecodeInfoCollector::CollectFunctionTypeId(panda_file::File::EntityId fieldId)
241 {
242     uint32_t offset = fieldId.GetOffset();
243     TypeAnnotationExtractor annoExtractor(jsPandaFile_, offset);
244     uint32_t typeId = annoExtractor.GetMethodTypeOffset();
245     if (typeId != 0) {
246         bytecodeInfo_.SetFunctionTypeIDAndMethodOffset(typeId, offset);
247     }
248     if (annoExtractor.IsNamespace()) {
249         MarkMethodNamespace(offset);
250     }
251 }
252 
CollectInnerFuncType(const MethodLiteral * method,uint32_t innerMethodId,int32_t bcIndex)253 void BytecodeInfoCollector::CollectInnerFuncType(const MethodLiteral *method, uint32_t innerMethodId, int32_t bcIndex)
254 {
255     auto &methodList = bytecodeInfo_.GetMethodList();
256     auto methodId = method->GetMethodId().GetOffset();
257     auto methodIter = methodList.find(methodId);
258     if (methodIter == methodList.end()) {
259         return;
260     }
261     TypeAnnotationExtractor annoExtractor(jsPandaFile_, innerMethodId);
262     uint32_t innerFuncType = annoExtractor.GetMethodTypeOffset();
263     if (innerFuncType != 0) {
264         methodIter->second.AddBcToTypeId(bcIndex, innerFuncType);
265     }
266 }
267 
IterateLiteral(const MethodLiteral * method,std::vector<uint32_t> & classOffsetVector)268 void BytecodeInfoCollector::IterateLiteral(const MethodLiteral *method,
269                                            std::vector<uint32_t> &classOffsetVector)
270 {
271     panda_file::File::EntityId fieldId = method->GetMethodId();
272     uint32_t defineMethodOffset = fieldId.GetOffset();
273     TypeAnnotationExtractor annoExtractor(jsPandaFile_, defineMethodOffset);
274     std::map<int32_t, uint32_t> offsetTypeMap;
275     annoExtractor.EnumerateInstsAndTypes(
276         [this, &offsetTypeMap, defineMethodOffset](const int32_t bcOffset, const uint32_t typeOffset) {
277             if (classDefBCIndexes_.find(bcOffset) != classDefBCIndexes_.end() ||
278                 classDefBCIndexes_.find(bcOffset - 1) != classDefBCIndexes_.end()) { // for getter setter
279                 bytecodeInfo_.SetClassTypeOffsetAndDefMethod(typeOffset, defineMethodOffset);
280             }
281             if (bcOffset != TypeRecorder::METHOD_ANNOTATION_THIS_TYPE_OFFSET &&
282                 TSTypeParser::IsUserDefinedType(typeOffset)) {
283                 offsetTypeMap.insert(std::make_pair(bcOffset, typeOffset));
284             }
285         });
286 
287     for (auto item : offsetTypeMap) {
288         uint32_t typeOffset = item.second;
289         StoreClassTypeOffset(typeOffset, classOffsetVector);
290     }
291 
292     classDefBCIndexes_.clear();
293 }
294 
StoreClassTypeOffset(const uint32_t typeOffset,std::vector<uint32_t> & classOffsetVector)295 void BytecodeInfoCollector::StoreClassTypeOffset(const uint32_t typeOffset, std::vector<uint32_t> &classOffsetVector)
296 {
297     TypeLiteralExtractor typeLiteralExtractor(jsPandaFile_, typeOffset);
298     if (typeLiteralExtractor.GetTypeKind() != TSTypeKind::CLASS) {
299         return;
300     }
301 
302     if (classOffsetVector.empty() || typeOffset != classOffsetVector.back()) {
303         classOffsetVector.emplace_back(typeOffset);
304     }
305 }
306 
CollectMethodPcsFromBC(const uint32_t insSz,const uint8_t * insArr,MethodLiteral * method,std::vector<std::string> & classNameVec,const CString & recordName,uint32_t methodOffset,std::vector<panda_file::File::EntityId> & classConstructIndexes)307 void BytecodeInfoCollector::CollectMethodPcsFromBC(const uint32_t insSz, const uint8_t *insArr,
308     MethodLiteral *method, std::vector<std::string> &classNameVec, const CString &recordName,
309     uint32_t methodOffset, std::vector<panda_file::File::EntityId> &classConstructIndexes)
310 {
311     auto bcIns = BytecodeInst(insArr);
312     auto bcInsLast = bcIns.JumpTo(insSz);
313     int32_t bcIndex = 0;
314     auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
315     methodPcInfos.emplace_back(MethodPcInfo { {}, insSz });
316     auto &pcOffsets = methodPcInfos.back().pcOffsets;
317     const uint8_t *curPc = bcIns.GetAddress();
318     bool canFastCall = true;
319     bool noGC = true;
320     bool debuggerStmt = false;
321 
322     while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
323         bool fastCallFlag = true;
324         CollectMethodInfoFromBC(bcIns, method, classNameVec, bcIndex, classConstructIndexes, &fastCallFlag);
325         if (!fastCallFlag) {
326             canFastCall = false;
327         }
328         CollectModuleInfoFromBC(bcIns, method, recordName);
329         snapshotCPData_.Record(bcIns, bcIndex, recordName, method);
330         pgoBCInfo_.Record(bcIns, bcIndex, recordName, method);
331         if (noGC && !bytecodes_.GetBytecodeMetaData(curPc).IsNoGC()) {
332             noGC = false;
333         }
334         if (!debuggerStmt && bytecodes_.GetBytecodeMetaData(curPc).HasDebuggerStmt()) {
335             debuggerStmt = true;
336         }
337         curPc = bcIns.GetAddress();
338         auto nextInst = bcIns.GetNext();
339         bcIns = nextInst;
340         pcOffsets.emplace_back(curPc);
341         bcIndex++;
342     }
343     bytecodeInfo_.SetMethodOffsetToFastCallInfo(methodOffset, canFastCall, noGC);
344     method->SetIsFastCall(canFastCall);
345     method->SetNoGCBit(noGC);
346     method->SetHasDebuggerStmtBit(debuggerStmt);
347 }
348 
SetMethodPcInfoIndex(uint32_t methodOffset,const std::pair<size_t,uint32_t> & processedMethodInfo)349 void BytecodeInfoCollector::SetMethodPcInfoIndex(uint32_t methodOffset,
350                                                  const std::pair<size_t, uint32_t> &processedMethodInfo)
351 {
352     auto processedMethodPcInfoIndex = processedMethodInfo.first;
353     auto processedMethodOffset = processedMethodInfo.second;
354     uint32_t numOfLexVars = 0;
355     LexicalEnvStatus status = LexicalEnvStatus::VIRTUAL_LEXENV;
356     auto &methodList = bytecodeInfo_.GetMethodList();
357     std::set<uint32_t> indexSet{};
358     // Methods with the same instructions in abc files have the same static information. Since
359     // information from bytecodes is collected only once, methods other than the processed method
360     // will obtain static information from the processed method.
361     auto processedIter = methodList.find(processedMethodOffset);
362     if (processedIter != methodList.end()) {
363         const MethodInfo &processedMethod = processedIter->second;
364         numOfLexVars = processedMethod.GetNumOfLexVars();
365         status = processedMethod.GetLexEnvStatus();
366         indexSet = processedMethod.GetImportIndexes();
367     }
368 
369     auto iter = methodList.find(methodOffset);
370     if (iter != methodList.end()) {
371         MethodInfo &methodInfo = iter->second;
372         methodInfo.SetMethodPcInfoIndex(processedMethodPcInfoIndex);
373         methodInfo.SetNumOfLexVars(numOfLexVars);
374         methodInfo.SetLexEnvStatus(status);
375         // if these methods have the same bytecode, their import indexs must be the same.
376         methodInfo.CopyImportIndex(indexSet);
377         return;
378     }
379     MethodInfo info(GetMethodInfoID(), processedMethodPcInfoIndex, LexEnv::DEFAULT_ROOT,
380         MethodInfo::DEFAULT_OUTMETHOD_OFFSET, numOfLexVars, status);
381     info.CopyImportIndex(indexSet);
382     methodList.emplace(methodOffset, info);
383 }
384 
CollectInnerMethods(const MethodLiteral * method,uint32_t innerMethodOffset,bool isConstructor)385 void BytecodeInfoCollector::CollectInnerMethods(const MethodLiteral *method,
386                                                 uint32_t innerMethodOffset,
387                                                 bool isConstructor)
388 {
389     auto methodId = method->GetMethodId().GetOffset();
390     CollectInnerMethods(methodId, innerMethodOffset, isConstructor);
391 }
392 
CollectInnerMethods(uint32_t methodId,uint32_t innerMethodOffset,bool isConstructor)393 void BytecodeInfoCollector::CollectInnerMethods(uint32_t methodId, uint32_t innerMethodOffset, bool isConstructor)
394 {
395     auto &methodList = bytecodeInfo_.GetMethodList();
396     uint32_t methodInfoId = 0;
397     auto methodIter = methodList.find(methodId);
398     if (methodIter != methodList.end()) {
399         MethodInfo &methodInfo = methodIter->second;
400         methodInfoId = methodInfo.GetMethodInfoIndex();
401         methodInfo.AddInnerMethod(innerMethodOffset, isConstructor);
402     } else {
403         methodInfoId = GetMethodInfoID();
404         MethodInfo info(methodInfoId, 0, LexEnv::DEFAULT_ROOT);
405         methodList.emplace(methodId, info);
406         methodList.at(methodId).AddInnerMethod(innerMethodOffset, isConstructor);
407     }
408 
409     auto innerMethodIter = methodList.find(innerMethodOffset);
410     if (innerMethodIter != methodList.end()) {
411         innerMethodIter->second.SetOutMethodId(methodInfoId);
412         innerMethodIter->second.SetOutMethodOffset(methodId);
413         return;
414     }
415     MethodInfo innerInfo(GetMethodInfoID(), 0, methodInfoId, methodId);
416     methodList.emplace(innerMethodOffset, innerInfo);
417 }
418 
MarkMethodNamespace(const uint32_t methodOffset)419 void BytecodeInfoCollector::MarkMethodNamespace(const uint32_t methodOffset)
420 {
421     auto &methodList = bytecodeInfo_.GetMethodList();
422     auto iter = methodList.find(methodOffset);
423     if (iter != methodList.end()) {
424         MethodInfo &methodInfo = iter->second;
425         methodInfo.MarkMethodNamespace();
426         return;
427     }
428     MethodInfo info(GetMethodInfoID(), 0, LexEnv::DEFAULT_ROOT, MethodInfo::DEFAULT_OUTMETHOD_OFFSET,
429         0, LexicalEnvStatus::VIRTUAL_LEXENV, true);
430     methodList.emplace(methodOffset, info);
431 }
432 
CollectInnerMethodsFromLiteral(const MethodLiteral * method,uint64_t index)433 void BytecodeInfoCollector::CollectInnerMethodsFromLiteral(const MethodLiteral *method, uint64_t index)
434 {
435     std::vector<uint32_t> methodOffsets;
436     LiteralDataExtractor::GetMethodOffsets(jsPandaFile_, index, methodOffsets);
437     for (auto methodOffset : methodOffsets) {
438         CollectInnerMethods(method, methodOffset);
439     }
440 }
441 
NewLexEnvWithSize(const MethodLiteral * method,uint64_t numOfLexVars)442 void BytecodeInfoCollector::NewLexEnvWithSize(const MethodLiteral *method, uint64_t numOfLexVars)
443 {
444     auto &methodList = bytecodeInfo_.GetMethodList();
445     auto methodOffset = method->GetMethodId().GetOffset();
446     auto iter = methodList.find(methodOffset);
447     if (iter != methodList.end()) {
448         MethodInfo &methodInfo = iter->second;
449         methodInfo.SetNumOfLexVars(numOfLexVars);
450         methodInfo.SetLexEnvStatus(LexicalEnvStatus::REALITY_LEXENV);
451         return;
452     }
453     MethodInfo info(GetMethodInfoID(), 0, LexEnv::DEFAULT_ROOT, MethodInfo::DEFAULT_OUTMETHOD_OFFSET,
454         numOfLexVars, LexicalEnvStatus::REALITY_LEXENV);
455     methodList.emplace(methodOffset, info);
456 }
457 
CollectInnerMethodsFromNewLiteral(const MethodLiteral * method,panda_file::File::EntityId literalId)458 void BytecodeInfoCollector::CollectInnerMethodsFromNewLiteral(const MethodLiteral *method,
459                                                               panda_file::File::EntityId literalId)
460 {
461     std::vector<uint32_t> methodOffsets;
462     LiteralDataExtractor::GetMethodOffsets(jsPandaFile_, literalId, methodOffsets);
463     for (auto methodOffset : methodOffsets) {
464         CollectInnerMethods(method, methodOffset);
465     }
466 }
467 
CollectMethodInfoFromBC(const BytecodeInstruction & bcIns,const MethodLiteral * method,std::vector<std::string> & classNameVec,int32_t bcIndex,std::vector<panda_file::File::EntityId> & classConstructIndexes,bool * canFastCall)468 void BytecodeInfoCollector::CollectMethodInfoFromBC(const BytecodeInstruction &bcIns,
469     const MethodLiteral *method, std::vector<std::string> &classNameVec, int32_t bcIndex,
470     std::vector<panda_file::File::EntityId> &classConstructIndexes, bool *canFastCall)
471 {
472     if (!(bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) &&
473         BytecodeInstruction::HasId(BytecodeInstruction::GetFormat(bcIns.GetOpcode()), 0))) {
474         BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
475         switch (opcode) {
476             uint32_t methodId;
477             case BytecodeInstruction::Opcode::DEFINEFUNC_IMM8_ID16_IMM8:
478             case BytecodeInstruction::Opcode::DEFINEFUNC_IMM16_ID16_IMM8: {
479                 methodId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
480                     static_cast<uint16_t>(bcIns.GetId().AsRawValue())).GetOffset();
481                 CollectInnerMethods(method, methodId);
482                 CollectInnerFuncType(method, methodId, bcIndex);
483                 break;
484             }
485             case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM8_ID16_IMM8:
486             case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM16_ID16_IMM8: {
487                 methodId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
488                     static_cast<uint16_t>(bcIns.GetId().AsRawValue())).GetOffset();
489                 CollectInnerMethods(method, methodId);
490                 break;
491             }
492             case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8:{
493                 auto entityId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
494                     (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
495                 classConstructIndexes.emplace_back(entityId);
496                 classNameVec.emplace_back(GetClassName(entityId));
497                 classDefBCIndexes_.insert(bcIndex);
498                 methodId = entityId.GetOffset();
499                 CollectInnerMethods(method, methodId, true);
500                 auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
501                     (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
502                 CollectInnerMethodsFromNewLiteral(method, literalId);
503                 break;
504             }
505             case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: {
506                 auto entityId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
507                     (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
508                 classConstructIndexes.emplace_back(entityId);
509                 classNameVec.emplace_back(GetClassName(entityId));
510                 classDefBCIndexes_.insert(bcIndex);
511                 methodId = entityId.GetOffset();
512                 CollectInnerMethods(method, methodId, true);
513                 auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
514                     (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
515                 CollectInnerMethodsFromNewLiteral(method, literalId);
516                 break;
517             }
518             case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
519             case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM16_ID16: {
520                 auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
521                     static_cast<uint16_t>(bcIns.GetId().AsRawValue()));
522                 CollectInnerMethodsFromNewLiteral(method, literalId);
523                 break;
524             }
525             case BytecodeInstruction::Opcode::DEPRECATED_CREATEARRAYWITHBUFFER_PREF_IMM16: {
526                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
527                 CollectInnerMethodsFromLiteral(method, imm);
528                 break;
529             }
530             case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM8_ID16:
531             case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: {
532                 auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
533                     static_cast<uint16_t>(bcIns.GetId().AsRawValue()));
534                 CollectInnerMethodsFromNewLiteral(method, literalId);
535                 break;
536             }
537             case BytecodeInstruction::Opcode::DEPRECATED_CREATEOBJECTWITHBUFFER_PREF_IMM16: {
538                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
539                 CollectInnerMethodsFromLiteral(method, imm);
540                 break;
541             }
542             case BytecodeInstruction::Opcode::NEWLEXENV_IMM8: {
543                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8>();
544                 NewLexEnvWithSize(method, imm);
545                 break;
546             }
547             case BytecodeInstruction::Opcode::NEWLEXENVWITHNAME_IMM8_ID16: {
548                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8_ID16>();
549                 NewLexEnvWithSize(method, imm);
550                 break;
551             }
552             case BytecodeInstruction::Opcode::WIDE_NEWLEXENV_PREF_IMM16: {
553                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
554                 NewLexEnvWithSize(method, imm);
555                 break;
556             }
557             case BytecodeInstruction::Opcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16: {
558                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16_ID16>();
559                 NewLexEnvWithSize(method, imm);
560                 break;
561             }
562             case EcmaOpcode::RESUMEGENERATOR:
563             case EcmaOpcode::SUSPENDGENERATOR_V8:
564             case EcmaOpcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
565             case EcmaOpcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
566             case EcmaOpcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
567             case EcmaOpcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
568             case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8:
569             case EcmaOpcode::GETUNMAPPEDARGS:
570             case EcmaOpcode::COPYRESTARGS_IMM8:
571             case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16: {
572                 *canFastCall = false;
573                 return;
574             }
575             default:
576                 break;
577         }
578     }
579 }
580 
CollectModuleInfoFromBC(const BytecodeInstruction & bcIns,const MethodLiteral * method,const CString & recordName)581 void BytecodeInfoCollector::CollectModuleInfoFromBC(const BytecodeInstruction &bcIns,
582                                                     const MethodLiteral *method,
583                                                     const CString &recordName)
584 {
585     auto methodOffset = method->GetMethodId().GetOffset();
586     // For records without tsType, we don't need to collect its export info.
587     if (jsPandaFile_->HasTSTypes(recordName) && !(bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) &&
588         BytecodeInstruction::HasId(BytecodeInstruction::GetFormat(bcIns.GetOpcode()), 0))) {
589         BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
590         switch (opcode) {
591             case BytecodeInstruction::Opcode::STMODULEVAR_IMM8: {
592                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8>();
593                 // The export syntax only exists in main function.
594                 if (jsPandaFile_->GetMainMethodIndex(recordName) == methodOffset) {
595                     CollectExportIndexs(recordName, imm);
596                 }
597                 break;
598             }
599             case BytecodeInstruction::Opcode::WIDE_STMODULEVAR_PREF_IMM16: {
600                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
601                 if (jsPandaFile_->GetMainMethodIndex(recordName) == methodOffset) {
602                     CollectExportIndexs(recordName, imm);
603                 }
604                 break;
605             }
606             case BytecodeInstruction::Opcode::LDEXTERNALMODULEVAR_IMM8:{
607                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8>();
608                 CollectImportIndexs(methodOffset, imm);
609                 break;
610             }
611             case BytecodeInstruction::Opcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16:{
612                 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
613                 CollectImportIndexs(methodOffset, imm);
614                 break;
615             }
616             default:
617                 break;
618         }
619     }
620 }
621 
CollectImportIndexs(uint32_t methodOffset,uint32_t index)622 void BytecodeInfoCollector::CollectImportIndexs(uint32_t methodOffset, uint32_t index)
623 {
624     auto &methodList = bytecodeInfo_.GetMethodList();
625     auto iter = methodList.find(methodOffset);
626     if (iter != methodList.end()) {
627         MethodInfo &methodInfo = iter->second;
628         // Collect import indexs of each method in its MethodInfo to do accurate Pgo compilation analysis.
629         methodInfo.AddImportIndex(index);
630         return;
631     }
632     MethodInfo info(GetMethodInfoID(), 0, LexEnv::DEFAULT_ROOT);
633     info.AddImportIndex(index);
634     methodList.emplace(methodOffset, info);
635 }
636 
CollectExportIndexs(const CString & recordName,uint32_t index)637 void BytecodeInfoCollector::CollectExportIndexs(const CString &recordName, uint32_t index)
638 {
639     JSThread *thread = vm_->GetJSThread();
640     ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
641     CString exportLocalName = "*default*";
642     ObjectFactory *objFactory = vm_->GetFactory();
643     [[maybe_unused]] EcmaHandleScope scope(thread);
644     JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
645     if (currentModule->GetLocalExportEntries().IsUndefined()) {
646         return;
647     }
648     // localExportEntries contain all local element info exported in this record.
649     JSHandle<TaggedArray> localExportArray(thread, currentModule->GetLocalExportEntries());
650     ASSERT(index < localExportArray->GetLength());
651     JSHandle<LocalExportEntry> currentExportEntry(thread, localExportArray->Get(index));
652     JSHandle<JSTaggedValue> exportName(thread, currentExportEntry->GetExportName());
653     JSHandle<JSTaggedValue> localName(thread, currentExportEntry->GetLocalName());
654 
655     JSHandle<JSTaggedValue> exportLocalNameHandle =
656         JSHandle<JSTaggedValue>::Cast(objFactory->NewFromUtf8(exportLocalName));
657     JSHandle<JSTaggedValue> defaultName = thread->GlobalConstants()->GetHandledDefaultString();
658     /* if current exportName is "default", but localName not "*default*" like "export default class A{},
659      * localName is A, exportName is default in exportEntry". this will be recorded as "A:classType" in
660      * exportTable in typeSystem. At this situation, we will use localName to judge whether it has a actual
661      * Type record. Otherwise, we will use exportName.
662      */
663     if (JSTaggedValue::SameValue(exportName, defaultName) &&
664         !JSTaggedValue::SameValue(localName, exportLocalNameHandle)) {
665         exportName = localName;
666     }
667 
668     JSHandle<EcmaString> exportNameStr(thread, EcmaString::Cast(exportName->GetTaggedObject()));
669     // In order to reduce redundant compilation, when a export element satisfies one of the following conditions,
670     // it will be added to the ExportRecordInfo of this record.
671     // 1) its name is not recorded in exportTypeTable, or
672     // 2) its type is classType or any.
673     if (!CheckExportNameAndClassType(recordName, exportNameStr)) {
674         bytecodeInfo_.AddExportIndexToRecord(recordName, index);
675     }
676 }
677 
CheckExportNameAndClassType(const CString & recordName,const JSHandle<EcmaString> & exportStr)678 bool BytecodeInfoCollector::CheckExportNameAndClassType(const CString &recordName,
679                                                         const JSHandle<EcmaString> &exportStr)
680 {
681     auto tsManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager();
682     JSHandle<TaggedArray> exportTypeTable = tsManager->GetExportTableFromLiteral(jsPandaFile_, recordName);
683     uint32_t length = exportTypeTable->GetLength();
684     for (uint32_t i = 0; i < length; i = i + 2) { // 2: skip a pair of key and value
685         EcmaString *valueString = EcmaString::Cast(exportTypeTable->Get(i).GetTaggedObject());
686         if (!EcmaStringAccessor::StringsAreEqual(*exportStr, valueString)) {
687             continue;
688         }
689         uint32_t typeId = static_cast<uint32_t>(exportTypeTable->Get(i + 1).GetInt());
690         if (TSTypeParser::IsUserDefinedType(typeId)) {
691             TypeLiteralExtractor typeExtractor(jsPandaFile_, typeId);
692             if (typeExtractor.GetTypeKind() == TSTypeKind::CLASS) {
693                 return false;
694             }
695         }
696         if (typeId != 0) {
697             return true;
698         }
699     }
700     return false;
701 }
702 
CollectRecordReferenceREL()703 void BytecodeInfoCollector::CollectRecordReferenceREL()
704 {
705     auto &recordNames = bytecodeInfo_.GetRecordNames();
706     for (auto &record : recordNames) {
707         JSRecordInfo info = jsPandaFile_->FindRecordInfo(record);
708         if (jsPandaFile_->HasTSTypes(info) && jsPandaFile_->IsModule(info)) {
709             CollectRecordImportInfo(record);
710             CollectRecordExportInfo(record);
711         }
712     }
713 }
714 
715 /* Each import index is corresponded to a ResolvedIndexBinding in the Environment of its module.
716  * Through ResolvedIndexBinding, we can get the export module and its export index. Only when the
717  * export index is in the non-type-record set which we have collected in CollectExportIndexs function,
718  * this export element can be infer-needed. We will collect the map as (key: import index , value: (exportRecord,
719  * exportIndex)) for using in pgo analysis and type infer.
720  */
CollectRecordImportInfo(const CString & recordName)721 void BytecodeInfoCollector::CollectRecordImportInfo(const CString &recordName)
722 {
723     auto thread = vm_->GetJSThread();
724     ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
725     [[maybe_unused]] EcmaHandleScope scope(thread);
726     JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
727     // Collect Import Info
728     JSTaggedValue moduleEnvironment = currentModule->GetEnvironment();
729     if (moduleEnvironment.IsUndefined()) {
730         return;
731     }
732     ASSERT(moduleEnvironment.IsTaggedArray());
733     JSHandle<TaggedArray> moduleArray(thread, moduleEnvironment);
734     auto length = moduleArray->GetLength();
735     for (size_t index = 0; index < length; index++) {
736         JSTaggedValue resolvedBinding = moduleArray->Get(index);
737         // if resolvedBinding.IsHole(), means that importname is * or it belongs to empty Aot module.
738         if (!resolvedBinding.IsResolvedIndexBinding()) {
739             continue;
740         }
741         ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
742         CString resolvedRecord = ModuleManager::GetRecordName(binding->GetModule());
743         auto bindingIndex = binding->GetIndex();
744         if (bytecodeInfo_.HasExportIndexToRecord(resolvedRecord, bindingIndex)) {
745             bytecodeInfo_.AddImportRecordInfoToRecord(recordName, resolvedRecord, index, bindingIndex);
746         }
747     }
748 }
749 
750 // For type infer under retranmission (export * from "xxx"), we collect the star export records in this function.
CollectRecordExportInfo(const CString & recordName)751 void BytecodeInfoCollector::CollectRecordExportInfo(const CString &recordName)
752 {
753     auto thread = vm_->GetJSThread();
754     ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
755     [[maybe_unused]] EcmaHandleScope scope(thread);
756     JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
757     // Collect Star Export Info
758     JSTaggedValue starEntries = currentModule->GetStarExportEntries();
759     if (starEntries.IsUndefined()) {
760         return;
761     }
762     ASSERT(starEntries.IsTaggedArray());
763     JSHandle<TaggedArray> starEntriesArray(thread, starEntries);
764     auto starLength = starEntriesArray->GetLength();
765     JSMutableHandle<StarExportEntry> starExportEntry(thread, JSTaggedValue::Undefined());
766     for (size_t index = 0; index < starLength; index++) {
767         starExportEntry.Update(starEntriesArray->Get(index));
768         JSTaggedValue moduleRequest = starExportEntry->GetModuleRequest();
769         CString moduleRequestName = ConvertToString(EcmaString::Cast(moduleRequest.GetTaggedObject()));
770         if (ModulePathHelper::IsNativeModuleRequest(moduleRequestName)) {
771             return;
772         }
773         CString baseFileName = jsPandaFile_->GetJSPandaFileDesc();
774         CString entryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, jsPandaFile_,
775             baseFileName, recordName, moduleRequestName);
776         if (jsPandaFile_->HasTypeSummaryOffset(entryPoint)) {
777             bytecodeInfo_.AddStarExportToRecord(recordName, entryPoint);
778         }
779     }
780 }
781 
RearrangeInnerMethods()782 void BytecodeInfoCollector::RearrangeInnerMethods()
783 {
784     auto &methodList = bytecodeInfo_.GetMethodList();
785     for (auto &it : methodList) {
786         MethodInfo &methodInfo = it.second;
787         methodInfo.RearrangeInnerMethods();
788     }
789 }
790 
LexEnvManager(BCInfo & bcInfo)791 LexEnvManager::LexEnvManager(BCInfo &bcInfo)
792     : lexEnvs_(bcInfo.GetMethodList().size())
793 {
794     const auto &methodList = bcInfo.GetMethodList();
795     for (const auto &it : methodList) {
796         const MethodInfo &methodInfo = it.second;
797         lexEnvs_[methodInfo.GetMethodInfoIndex()].Inilialize(methodInfo.GetOutMethodId(),
798                                                              methodInfo.GetNumOfLexVars(),
799                                                              methodInfo.GetLexEnvStatus());
800     }
801 }
802 
SetLexEnvElementType(uint32_t methodId,uint32_t level,uint32_t slot,const GateType & type)803 void LexEnvManager::SetLexEnvElementType(uint32_t methodId, uint32_t level, uint32_t slot, const GateType &type)
804 {
805     uint32_t offset = GetTargetLexEnv(methodId, level);
806     lexEnvs_[offset].SetLexVarType(slot, type);
807 }
808 
GetLexEnvElementType(uint32_t methodId,uint32_t level,uint32_t slot) const809 GateType LexEnvManager::GetLexEnvElementType(uint32_t methodId, uint32_t level, uint32_t slot) const
810 {
811     uint32_t offset = GetTargetLexEnv(methodId, level);
812     return lexEnvs_[offset].GetLexVarType(slot);
813 }
814 
GetTargetLexEnv(uint32_t methodId,uint32_t level) const815 uint32_t LexEnvManager::GetTargetLexEnv(uint32_t methodId, uint32_t level) const
816 {
817     auto offset = methodId;
818     auto status = GetLexEnvStatus(offset);
819     while (!HasDefaultRoot(offset) && ((level > 0) || (status != LexicalEnvStatus::REALITY_LEXENV))) {
820         offset = GetOutMethodId(offset);
821         if (HasDefaultRoot(offset)) {
822             break;
823         }
824         if (status == LexicalEnvStatus::REALITY_LEXENV && level != 0) {
825             --level;
826         }
827         status = GetLexEnvStatus(offset);
828     }
829     return offset;
830 }
831 }  // namespace panda::ecmascript::kungfu
832