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