1/* 2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/jspandafile/method_literal.h" 17 18#include "ecmascript/interpreter/interpreter.h" 19#include "ecmascript/jspandafile/js_pandafile.h" 20 21#include "libpandafile/class_data_accessor.h" 22#include "libpandafile/code_data_accessor-inl.h" 23#include "libpandafile/method_data_accessor-inl.h" 24 25namespace panda::ecmascript { 26MethodLiteral::MethodLiteral(EntityId methodId) 27{ 28 ASSERT(methodId.IsValid()); 29 SetMethodId(methodId); 30} 31 32void MethodLiteral::Initialize(const JSPandaFile *jsPandaFile, const JSThread *thread) 33{ 34 const panda_file::File *pf = jsPandaFile->GetPandaFile(); 35 EntityId methodId = GetMethodId(); 36 panda_file::MethodDataAccessor mda(*pf, methodId); 37 auto codeId = mda.GetCodeId().value(); 38 ASSERT(codeId.IsValid()); 39 40 panda_file::CodeDataAccessor cda(*pf, codeId); 41 nativePointerOrBytecodeArray_ = cda.GetInstructions(); 42 uint32_t codeSize = cda.GetCodeSize(); 43 // When triggering jit compile through the execution count of the js function, set the hotness counter value to 0 44 // to ensure that the profile type info object can be created on the first execution of the js function. 45 bool cancelThreshold = (thread != nullptr && thread->GetEcmaVM()->GetJSOptions().GetJitCallThreshold() != 0); 46 SetHotnessCounter(EcmaInterpreter::GetHotnessCounter(codeSize, cancelThreshold)); 47 48 uint32_t callType = UINT32_MAX; // UINT32_MAX means not found 49 uint32_t slotSize = 0; 50 mda.EnumerateAnnotations([&](EntityId annotationId) { 51 panda_file::AnnotationDataAccessor ada(*pf, annotationId); 52 auto *annotationName = reinterpret_cast<const char *>(pf->GetStringData(ada.GetClassId()).data); 53 if (::strcmp("L_ESCallTypeAnnotation;", annotationName) == 0) { 54 uint32_t elemCount = ada.GetCount(); 55 for (uint32_t i = 0; i < elemCount; i++) { 56 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i); 57 auto *elemName = reinterpret_cast<const char *>(pf->GetStringData(adae.GetNameId()).data); 58 if (::strcmp("callType", elemName) == 0) { 59 callType = adae.GetScalarValue().GetValue(); 60 } 61 } 62 } else if (::strcmp("L_ESSlotNumberAnnotation;", annotationName) == 0) { 63 uint32_t elemCount = ada.GetCount(); 64 for (uint32_t i = 0; i < elemCount; i++) { 65 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i); 66 auto *elemName = reinterpret_cast<const char *>(pf->GetStringData(adae.GetNameId()).data); 67 if (::strcmp("SlotNumber", elemName) == 0) { 68 slotSize = adae.GetScalarValue().GetValue(); 69 } 70 } 71 } 72 }); 73 74 uint32_t numVregs = cda.GetNumVregs(); 75 uint32_t numArgs = cda.GetNumArgs(); 76 ASSERT((numArgs - HaveFuncBit::Decode(callType) - 77 HaveNewTargetBit::Decode(callType) - HaveThisBit::Decode(callType)) >= 0); 78 // Needed info for call can be got by loading callField only once. 79 // Native bit will be set in NewMethodForNativeFunction(); 80 callField_ = (callType & CALL_TYPE_MASK) | 81 NumVregsBits::Encode(numVregs) | 82 NumArgsBits::Encode(numArgs - HaveFuncBit::Decode(callType) // exclude func 83 - HaveNewTargetBit::Decode(callType) // exclude new target 84 - HaveThisBit::Decode(callType)); // exclude this 85 SetSlotSize(slotSize); 86} 87 88// It's not allowed '#' token appear in ECMA function(method) name, which discriminates same names in panda methods. 89std::string MethodLiteral::ParseFunctionName(const JSPandaFile* jsPandaFile, EntityId methodId) 90{ 91 std::string_view methodName = ParseFunctionNameView(jsPandaFile, methodId).first; 92 return std::string(methodName); 93} 94 95// It's not allowed '#' token appear in ECMA function(method) name, which discriminates same names in panda methods. 96std::pair<std::string_view, bool> MethodLiteral::ParseFunctionNameView( 97 const JSPandaFile* jsPandaFile, EntityId methodId) 98{ 99 if (UNLIKELY(jsPandaFile == nullptr)) { 100 return {"", true}; 101 } 102 103 auto [methodName, isASCII] = GetMethodNameView(jsPandaFile, methodId); 104 if (LIKELY(methodName[0] != '#')) { 105 return {methodName, isASCII}; 106 } 107 108 size_t index = methodName.find_last_of('#'); 109 methodName = methodName.substr(index + 1); // #...#functionName 110 if (index = methodName.find_last_of('^'); index != std::string::npos) { 111 methodName = methodName.substr(0, index); // #...#functionName^1 112 } 113 return {methodName, isASCII}; 114} 115 116// It's not allowed '#' token appear in ECMA function(method) name, which discriminates same names in panda methods. 117CString MethodLiteral::ParseFunctionNameToCString(const JSPandaFile *jsPandaFile, EntityId methodId) 118{ 119 if (jsPandaFile == nullptr) { 120 return ""; 121 } 122 123 CString methodName(GetMethodName(jsPandaFile, methodId)); 124 if (LIKELY(methodName[0] != '#')) { 125 return methodName; 126 } 127 128 size_t index = methodName.find_last_of('#'); 129 methodName = methodName.substr(index + 1); // #...#functionName 130 if (methodName.find('^') != std::string::npos) { 131 index = methodName.find_last_of('^'); 132 methodName = methodName.substr(0, index); // #...#functionName^1 133 } 134 return methodName; 135} 136 137const char* MethodLiteral::GetMethodName(const JSPandaFile* jsPandaFile, EntityId methodId, bool cpuProfiler) 138{ 139 if (jsPandaFile == nullptr) { 140 return ""; 141 } 142 return GetMethodNameView(jsPandaFile, methodId, cpuProfiler).first.data(); 143} 144 145std::pair<std::string_view, bool> MethodLiteral::GetMethodNameView( 146 const JSPandaFile* jsPandaFile, EntityId methodId, bool cpuProfiler) 147{ 148 ASSERT(jsPandaFile != nullptr && "jsPandaFile is null"); 149 if (cpuProfiler) { 150 return jsPandaFile->GetCpuProfilerMethodName(methodId); 151 } 152 return const_cast<JSPandaFile*>(jsPandaFile)->GetMethodName(methodId); 153} 154 155CString MethodLiteral::GetRecordName(const JSPandaFile *jsPandaFile, EntityId methodId) 156{ 157 if (jsPandaFile == nullptr) { 158 return ""; 159 } 160 161 return const_cast<JSPandaFile *>(jsPandaFile)->GetRecordName(methodId); 162} 163 164const char *MethodLiteral::GetRecordNameWithSymbol(const JSPandaFile *jsPandaFile, EntityId methodId) 165{ 166 if (jsPandaFile == nullptr) { 167 return ""; 168 } 169 170 const panda_file::File *pf = jsPandaFile->GetPandaFile(); 171 panda_file::MethodDataAccessor mda(*pf, methodId); 172 panda_file::ClassDataAccessor cda(*pf, mda.GetClassId()); 173 return utf::Mutf8AsCString(cda.GetDescriptor()); 174} 175 176uint32_t MethodLiteral::GetCodeSize(const JSPandaFile *jsPandaFile, EntityId methodId) 177{ 178 if (jsPandaFile == nullptr) { 179 return 0; 180 } 181 182 const panda_file::File *pandaFile = jsPandaFile->GetPandaFile(); 183 panda_file::MethodDataAccessor mda(*pandaFile, methodId); 184 auto codeId = mda.GetCodeId().value(); 185 if (!codeId.IsValid()) { 186 return 0; 187 } 188 189 panda_file::CodeDataAccessor cda(*pandaFile, codeId); 190 return cda.GetCodeSize(); 191} 192 193std::optional<std::set<uint32_t>> MethodLiteral::GetConcurrentRequestedModules(const JSPandaFile *jsPandaFile) const 194{ 195 ASSERT(jsPandaFile != nullptr); 196 const panda_file::File *pf = jsPandaFile->GetPandaFile(); 197 EntityId methodId = GetMethodId(); 198 panda_file::MethodDataAccessor mda(*pf, methodId); 199 std::set<uint32_t> requestedModules; 200 bool hasRequestedModules = false; 201 mda.EnumerateAnnotations([&](EntityId annotationId) { 202 panda_file::AnnotationDataAccessor ada(*pf, annotationId); 203 auto *annotationName = reinterpret_cast<const char *>(pf->GetStringData(ada.GetClassId()).data); 204 if (::strcmp("L_ESConcurrentModuleRequestsAnnotation;", annotationName) == 0) { 205 hasRequestedModules = true; 206 uint32_t elemCount = ada.GetCount(); 207 for (uint32_t i = 0; i < elemCount; i++) { 208 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i); 209 auto *elemName = reinterpret_cast<const char *>(pf->GetStringData(adae.GetNameId()).data); 210 if (::strcmp("ConcurrentModuleRequest", elemName) == 0) { 211 uint32_t index = adae.GetScalarValue().GetValue(); 212 requestedModules.insert(index); 213 } 214 } 215 } 216 }); 217 if (!hasRequestedModules) { 218 return std::nullopt; 219 } 220 return requestedModules; 221} 222} // namespace panda::ecmascript 223