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)32 void 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.
ParseFunctionName(const JSPandaFile* jsPandaFile, EntityId methodId)89 std::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.
ParseFunctionNameView( const JSPandaFile* jsPandaFile, EntityId methodId)96 std::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.
ParseFunctionNameToCString(const JSPandaFile *jsPandaFile, EntityId methodId)117 CString 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
GetMethodName(const JSPandaFile* jsPandaFile, EntityId methodId, bool cpuProfiler)137 const 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
GetMethodNameView( const JSPandaFile* jsPandaFile, EntityId methodId, bool cpuProfiler)145 std::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
GetRecordName(const JSPandaFile *jsPandaFile, EntityId methodId)155 CString 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
GetRecordNameWithSymbol(const JSPandaFile *jsPandaFile, EntityId methodId)164 const 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
GetCodeSize(const JSPandaFile *jsPandaFile, EntityId methodId)176 uint32_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
GetConcurrentRequestedModules(const JSPandaFile *jsPandaFile) const193 std::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