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