• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 namespace panda::ecmascript {
MethodLiteral(EntityId methodId)26 MethodLiteral::MethodLiteral(EntityId methodId)
27 {
28     ASSERT(methodId.IsValid());
29     SetMethodId(methodId);
30 }
31 
Initialize(const JSPandaFile * jsPandaFile,const JSThread * thread,const uint32_t offset)32 void MethodLiteral::Initialize(const JSPandaFile *jsPandaFile, const JSThread *thread, const uint32_t offset)
33 {
34     const panda_file::File *pf = jsPandaFile->GetPandaFile();
35     EntityId methodId = GetMethodId();
36     if (UNLIKELY(offset != 0 && methodId.GetOffset() != offset)) {
37         LOG_ECMA(FATAL) << "Invalid methodId, expected methodId: " << offset << ", actual methodId: " << methodId;
38     }
39     panda_file::MethodDataAccessor mda(*pf, methodId);
40     auto codeId = mda.GetCodeId().value();
41     ASSERT(codeId.IsValid());
42 
43     panda_file::CodeDataAccessor cda(*pf, codeId);
44     nativePointerOrBytecodeArray_ = cda.GetInstructions();
45     uint32_t codeSize = cda.GetCodeSize();
46     // When triggering jit compile only through the execution count of the js function, set the hotness counter
47     // value to 0, to ensure that the profile type info object can be created on the first execution of the js function.
48     bool cancelThreshold = (thread != nullptr && thread->GetEcmaVM()->GetJSOptions().GetJitHotnessThreshold() == 0);
49     SetHotnessCounter(EcmaInterpreter::GetHotnessCounter(codeSize, cancelThreshold));
50 
51     uint32_t callType = UINT32_MAX;  // UINT32_MAX means not found
52     uint32_t slotSize = 0;
53     uint32_t expectedPropertyCount = MAX_EXPECTED_PROPERTY_COUNT; // MAX_EXPECTED_PROPERTY_COUNT means not found
54     mda.EnumerateAnnotations([&](EntityId annotationId) {
55         panda_file::AnnotationDataAccessor ada(*pf, annotationId);
56         auto classIdstr = pf->GetStringData(ada.GetClassId());
57         std::string_view annotationNameView(utf::Mutf8AsCString(classIdstr.data), classIdstr.utf16_length);
58         if (annotationNameView == KCALL_TYPE_ANNOTATION) {
59             uint32_t elemCount = ada.GetCount();
60             for (uint32_t i = 0; i < elemCount; i++) {
61                 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
62                 auto nameStr = pf->GetStringData(adae.GetNameId());
63                 std::string_view elemNameView(utf::Mutf8AsCString(nameStr.data), nameStr.utf16_length);
64                 if (elemNameView == KCALL_TYPE_NAME) {
65                     callType = adae.GetScalarValue().GetValue();
66                     break;
67                 }
68             }
69         } else if (annotationNameView == KSLOT_NUMBER_ANNOTATION) {
70             uint32_t elemCount = ada.GetCount();
71             for (uint32_t i = 0; i < elemCount; i++) {
72                 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
73                 auto nameStr = pf->GetStringData(adae.GetNameId());
74                 std::string_view elemNameView(utf::Mutf8AsCString(nameStr.data), nameStr.utf16_length);
75                 if (elemNameView == KSLOT_NUMBER_NAME) {
76                     slotSize = adae.GetScalarValue().GetValue();
77                     break;
78                 }
79             }
80         } else if (annotationNameView == KEXPECTED_PROPERTY_COUNT_ANNOTATION) {
81             uint32_t elemCount = ada.GetCount();
82             for (uint32_t i = 0; i < elemCount; i++) {
83                 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
84                 auto nameStr = pf->GetStringData(adae.GetNameId());
85                 auto elemNameView = std::string_view(utf::Mutf8AsCString(nameStr.data), nameStr.utf16_length);
86                 if (elemNameView == KEXPECTED_PROPERTY_COUNT_NAME) {
87                     expectedPropertyCount = adae.GetScalarValue().GetValue();
88                     break;
89                 }
90             }
91         }
92     });
93 
94     uint32_t numVregs = cda.GetNumVregs();
95     uint32_t numArgs = cda.GetNumArgs();
96     ASSERT((numArgs - HaveFuncBit::Decode(callType) -
97         HaveNewTargetBit::Decode(callType) - HaveThisBit::Decode(callType)) >= 0);
98     // Needed info for call can be got by loading callField only once.
99     // Native bit will be set in NewMethodForNativeFunction();
100     callField_ = (callType & CALL_TYPE_MASK) |
101                  NumVregsBits::Encode(numVregs) |
102                  NumArgsBits::Encode(numArgs - HaveFuncBit::Decode(callType)  // exclude func
103                                              - HaveNewTargetBit::Decode(callType)  // exclude new target
104                                              - HaveThisBit::Decode(callType));  // exclude this
105     SetExpectedPropertyCount(expectedPropertyCount);
106     SetSlotSize(slotSize);
107 }
108 
109 // It's not allowed '#' token appear in ECMA function(method) name, which discriminates same names in panda methods.
ParseFunctionName(const JSPandaFile * jsPandaFile,EntityId methodId)110 std::string MethodLiteral::ParseFunctionName(const JSPandaFile* jsPandaFile, EntityId methodId)
111 {
112     std::string_view methodName = ParseFunctionNameView(jsPandaFile, methodId).first;
113     return std::string(methodName);
114 }
115 
116 // It's not allowed '#' token appear in ECMA function(method) name, which discriminates same names in panda methods.
ParseFunctionNameView(const JSPandaFile * jsPandaFile,EntityId methodId)117 std::pair<std::string_view, bool> MethodLiteral::ParseFunctionNameView(
118     const JSPandaFile* jsPandaFile, EntityId methodId)
119 {
120     if (UNLIKELY(jsPandaFile == nullptr)) {
121         return {"", true};
122     }
123 
124     auto [methodName, isASCII] = GetMethodNameView(jsPandaFile, methodId);
125     if (LIKELY(methodName[0] != '#')) {
126         return {methodName, isASCII};
127     }
128 
129     size_t index = methodName.find_last_of('#');
130     methodName = methodName.substr(index + 1); // #...#functionName
131     if (index = methodName.find_last_of('^'); index != std::string::npos) {
132         methodName = methodName.substr(0, index); // #...#functionName^1
133     }
134     return {methodName, isASCII};
135 }
136 
137 // It's not allowed '#' token appear in ECMA function(method) name, which discriminates same names in panda methods.
ParseFunctionNameToCString(const JSPandaFile * jsPandaFile,EntityId methodId)138 CString MethodLiteral::ParseFunctionNameToCString(const JSPandaFile *jsPandaFile, EntityId methodId)
139 {
140     if (jsPandaFile == nullptr) {
141         return "";
142     }
143 
144     CString methodName(GetMethodName(jsPandaFile, methodId));
145     if (LIKELY(methodName[0] != '#')) {
146         return methodName;
147     }
148 
149     size_t index = methodName.find_last_of('#');
150     methodName = methodName.substr(index + 1);  // #...#functionName
151     if (methodName.find('^') != std::string::npos) {
152         index = methodName.find_last_of('^');
153         methodName = methodName.substr(0, index);  // #...#functionName^1
154     }
155     return methodName;
156 }
157 
GetMethodName(const JSPandaFile * jsPandaFile,EntityId methodId,bool cpuProfiler)158 const char* MethodLiteral::GetMethodName(const JSPandaFile* jsPandaFile, EntityId methodId, bool cpuProfiler)
159 {
160     if (jsPandaFile == nullptr) {
161         return "";
162     }
163     return GetMethodNameView(jsPandaFile, methodId, cpuProfiler).first.data();
164 }
165 
GetMethodNameView(const JSPandaFile * jsPandaFile,EntityId methodId,bool cpuProfiler)166 std::pair<std::string_view, bool> MethodLiteral::GetMethodNameView(
167     const JSPandaFile* jsPandaFile, EntityId methodId, bool cpuProfiler)
168 {
169     ASSERT(jsPandaFile != nullptr && "jsPandaFile is null");
170     if (cpuProfiler) {
171         return jsPandaFile->GetCpuProfilerMethodName(methodId);
172     }
173     return const_cast<JSPandaFile*>(jsPandaFile)->GetMethodName(methodId);
174 }
175 
GetRecordName(const JSPandaFile * jsPandaFile,EntityId methodId)176 CString MethodLiteral::GetRecordName(const JSPandaFile *jsPandaFile, EntityId methodId)
177 {
178     if (jsPandaFile == nullptr) {
179         return "";
180     }
181 
182     return const_cast<JSPandaFile *>(jsPandaFile)->GetRecordName(methodId);
183 }
184 
GetRecordNameWithSymbol(const JSPandaFile * jsPandaFile,EntityId methodId)185 const char *MethodLiteral::GetRecordNameWithSymbol(const JSPandaFile *jsPandaFile, EntityId methodId)
186 {
187     if (jsPandaFile == nullptr) {
188         return "";
189     }
190 
191     const panda_file::File *pf = jsPandaFile->GetPandaFile();
192     panda_file::MethodDataAccessor mda(*pf, methodId);
193     panda_file::ClassDataAccessor cda(*pf, mda.GetClassId());
194     return utf::Mutf8AsCString(cda.GetDescriptor());
195 }
196 
GetCodeSize(const JSPandaFile * jsPandaFile,EntityId methodId)197 uint32_t MethodLiteral::GetCodeSize(const JSPandaFile *jsPandaFile, EntityId methodId)
198 {
199     if (jsPandaFile == nullptr) {
200         return 0;
201     }
202 
203     const panda_file::File *pandaFile = jsPandaFile->GetPandaFile();
204     panda_file::MethodDataAccessor mda(*pandaFile, methodId);
205     auto codeId = mda.GetCodeId().value();
206     if (!codeId.IsValid()) {
207         return 0;
208     }
209 
210     panda_file::CodeDataAccessor cda(*pandaFile, codeId);
211     return cda.GetCodeSize();
212 }
213 
GetConcurrentRequestedModules(const JSPandaFile * jsPandaFile) const214 std::optional<std::set<uint32_t>> MethodLiteral::GetConcurrentRequestedModules(const JSPandaFile *jsPandaFile) const
215 {
216     ASSERT(jsPandaFile != nullptr);
217     const panda_file::File *pf = jsPandaFile->GetPandaFile();
218     EntityId methodId = GetMethodId();
219     panda_file::MethodDataAccessor mda(*pf, methodId);
220     std::set<uint32_t> requestedModules;
221     bool hasRequestedModules = false;
222     mda.EnumerateAnnotations([&](EntityId annotationId) {
223         panda_file::AnnotationDataAccessor ada(*pf, annotationId);
224         auto *annotationName = reinterpret_cast<const char *>(pf->GetStringData(ada.GetClassId()).data);
225         if (::strcmp("L_ESConcurrentModuleRequestsAnnotation;", annotationName) == 0) {
226             hasRequestedModules = true;
227             uint32_t elemCount = ada.GetCount();
228             for (uint32_t i = 0; i < elemCount; i++) {
229                 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
230                 auto *elemName = reinterpret_cast<const char *>(pf->GetStringData(adae.GetNameId()).data);
231                 if (::strcmp("ConcurrentModuleRequest", elemName) == 0) {
232                     uint32_t index = adae.GetScalarValue().GetValue();
233                     requestedModules.insert(index);
234                 }
235             }
236         }
237     });
238     if (!hasRequestedModules) {
239         return std::nullopt;
240     }
241     return requestedModules;
242 }
243 } // namespace panda::ecmascript
244