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 #ifndef ECMASCRIPT_JSPANDAFILE_METHOD_LITERAL_H
17 #define ECMASCRIPT_JSPANDAFILE_METHOD_LITERAL_H
18 
19 #include <set>
20 
21 #include "ecmascript/base/aligned_struct.h"
22 #include "ecmascript/js_function_kind.h"
23 #include "ecmascript/js_tagged_value.h"
24 #include "ecmascript/mem/c_string.h"
25 #include "libpandafile/file.h"
26 
27 static constexpr uint32_t CALL_TYPE_MASK = 0xF;  // 0xF: the last 4 bits are used as callType
28 
29 namespace panda::ecmascript {
30 class JSPandaFile;
31 using EntityId = panda_file::File::EntityId;
32 using StringData = panda_file::File::StringData;
33 struct PUBLIC_API MethodLiteral : public base::AlignedStruct<sizeof(uint64_t),
34                                                         base::AlignedUint64,
35                                                         base::AlignedPointer,
36                                                         base::AlignedUint64,
37                                                         base::AlignedUint64> {
38 public:
39     static constexpr uint8_t INVALID_IC_SLOT = 0xFFU;
40     static constexpr uint16_t MAX_SLOT_SIZE = 0xFFFFU;
41     static constexpr size_t EXTEND_SLOT_SIZE = 2;
42 
43     PUBLIC_API explicit MethodLiteral(EntityId methodId);
44     MethodLiteral() = delete;
45     ~MethodLiteral() = default;
46 
47     NO_COPY_SEMANTIC(MethodLiteral);
48     NO_MOVE_SEMANTIC(MethodLiteral);
49 
50     static constexpr size_t VREGS_ARGS_NUM_BITS = 28; // 28: maximum 268,435,455
51     using HaveThisBit = BitField<bool, 0, 1>;  // offset 0
52     using HaveNewTargetBit = HaveThisBit::NextFlag;  // offset 1
53     using HaveExtraBit = HaveNewTargetBit::NextFlag;  // offset 2
54     using HaveFuncBit = HaveExtraBit::NextFlag;  // offset 3
55     using NumVregsBits = HaveFuncBit::NextField<uint32_t, VREGS_ARGS_NUM_BITS>;  // offset 4-31
56     using NumArgsBits = NumVregsBits::NextField<uint32_t, VREGS_ARGS_NUM_BITS>;  // offset 32-59
57     using IsNativeBit = NumArgsBits::NextFlag;  // offset 60
58     using IsAotCodeBit = IsNativeBit::NextFlag; // offset 61
59     using IsFastBuiltinBit = IsAotCodeBit::NextFlag; // offset 62
60     using IsFastCallBit = IsFastBuiltinBit::NextFlag; // offset 63
61 
GetCallFieldpanda::ecmascript::MethodLiteral62     uint64_t GetCallField() const
63     {
64         return callField_;
65     }
66 
SetNativeBitpanda::ecmascript::MethodLiteral67     void SetNativeBit(bool isNative)
68     {
69         callField_ = IsNativeBit::Update(callField_, isNative);
70     }
71 
SetAotCodeBitpanda::ecmascript::MethodLiteral72     void SetAotCodeBit(bool isCompiled)
73     {
74         callField_ = IsAotCodeBit::Update(callField_, isCompiled);
75     }
76 
77     void PUBLIC_API Initialize(const JSPandaFile *jsPandaFile, const JSThread *thread = nullptr);
78 
HaveThisWithCallFieldpanda::ecmascript::MethodLiteral79     bool HaveThisWithCallField() const
80     {
81         return HaveThisWithCallField(callField_);
82     }
83 
HaveNewTargetWithCallFieldpanda::ecmascript::MethodLiteral84     bool HaveNewTargetWithCallField() const
85     {
86         return HaveNewTargetWithCallField(callField_);
87     }
88 
HaveExtraWithCallFieldpanda::ecmascript::MethodLiteral89     bool HaveExtraWithCallField() const
90     {
91         return HaveExtraWithCallField(callField_);
92     }
93 
HaveFuncWithCallFieldpanda::ecmascript::MethodLiteral94     bool HaveFuncWithCallField() const
95     {
96         return HaveFuncWithCallField(callField_);
97     }
98 
IsNativeWithCallFieldpanda::ecmascript::MethodLiteral99     bool IsNativeWithCallField() const
100     {
101         return IsNativeWithCallField(callField_);
102     }
103 
GetNumArgsWithCallFieldpanda::ecmascript::MethodLiteral104     uint32_t GetNumArgsWithCallField() const
105     {
106         return GetNumArgsWithCallField(callField_);
107     }
108 
GetNumArgspanda::ecmascript::MethodLiteral109     uint32_t GetNumArgs() const
110     {
111         return GetNumArgsWithCallField() + HaveFuncWithCallField() +
112             HaveNewTargetWithCallField() + HaveThisWithCallField();
113     }
114 
GetNumberVRegspanda::ecmascript::MethodLiteral115     uint32_t GetNumberVRegs() const
116     {
117         return GetNumVregsWithCallField() + GetNumArgs();
118     }
119 
GetNewTargetVregIndexpanda::ecmascript::MethodLiteral120     uint32_t GetNewTargetVregIndex() const
121     {
122         if (!HaveNewTargetWithCallField()) {
123             return 0;
124         }
125         uint32_t numVregs = GetNumVregsWithCallField();
126         return HaveFuncWithCallField() ? (numVregs + 1) : numVregs;
127     }
128 
SetNativeBitpanda::ecmascript::MethodLiteral129     static uint64_t SetNativeBit(uint64_t callField, bool isNative)
130     {
131         return IsNativeBit::Update(callField, isNative);
132     }
133 
SetAotCodeBitpanda::ecmascript::MethodLiteral134     static uint64_t SetAotCodeBit(uint64_t callField, bool isCompiled)
135     {
136         return IsAotCodeBit::Update(callField, isCompiled);
137     }
138 
HaveThisWithCallFieldpanda::ecmascript::MethodLiteral139     static bool HaveThisWithCallField(uint64_t callField)
140     {
141         return HaveThisBit::Decode(callField);
142     }
143 
HaveNewTargetWithCallFieldpanda::ecmascript::MethodLiteral144     static bool HaveNewTargetWithCallField(uint64_t callField)
145     {
146         return HaveNewTargetBit::Decode(callField);
147     }
148 
HaveExtraWithCallFieldpanda::ecmascript::MethodLiteral149     static bool HaveExtraWithCallField(uint64_t callField)
150     {
151         return HaveExtraBit::Decode(callField);
152     }
153 
HaveFuncWithCallFieldpanda::ecmascript::MethodLiteral154     static bool HaveFuncWithCallField(uint64_t callField)
155     {
156         return HaveFuncBit::Decode(callField);
157     }
158 
IsNativeWithCallFieldpanda::ecmascript::MethodLiteral159     static bool IsNativeWithCallField(uint64_t callField)
160     {
161         return IsNativeBit::Decode(callField);
162     }
163 
IsAotWithCallFieldpanda::ecmascript::MethodLiteral164     static bool IsAotWithCallField(uint64_t callField)
165     {
166         return IsAotCodeBit::Decode(callField);
167     }
168 
OnlyHaveThisWithCallFieldpanda::ecmascript::MethodLiteral169     static bool OnlyHaveThisWithCallField(uint64_t callField)
170     {
171         return (callField & CALL_TYPE_MASK) == 1;  // 1: the first bit of callFiled is HaveThisBit
172     }
173 
OnlyHaveNewTagetAndThisWithCallFieldpanda::ecmascript::MethodLiteral174     static bool OnlyHaveNewTagetAndThisWithCallField(uint64_t callField)
175     {
176         return (callField & CALL_TYPE_MASK) == 0b11;  // the first two bit of callFiled is `This` and `NewTarget`
177     }
178 
GetNumVregsWithCallFieldpanda::ecmascript::MethodLiteral179     static uint32_t GetNumVregsWithCallField(uint64_t callField)
180     {
181         return NumVregsBits::Decode(callField);
182     }
183 
GetNumVregsWithCallFieldpanda::ecmascript::MethodLiteral184     uint32_t GetNumVregsWithCallField() const
185     {
186         return NumVregsBits::Decode(callField_);
187     }
188 
GetNumArgsWithCallFieldpanda::ecmascript::MethodLiteral189     static uint32_t GetNumArgsWithCallField(uint64_t callField)
190     {
191         return NumArgsBits::Decode(callField);
192     }
193 
SetIsFastCallpanda::ecmascript::MethodLiteral194     static uint64_t SetIsFastCall(uint64_t callField, bool isFastCall)
195     {
196         return IsFastCallBit::Update(callField, isFastCall);
197     }
198 
SetIsFastCallpanda::ecmascript::MethodLiteral199     void SetIsFastCall(bool isFastCall)
200     {
201         callField_ = IsFastCallBit::Update(callField_, isFastCall);
202     }
203 
IsFastCallpanda::ecmascript::MethodLiteral204     static bool IsFastCall(uint64_t callField)
205     {
206         return IsFastCallBit::Decode(callField);
207     }
208 
IsFastCallpanda::ecmascript::MethodLiteral209     bool IsFastCall() const
210     {
211         return IsFastCallBit::Decode(callField_);
212     }
213 
214     static constexpr size_t METHOD_ARGS_NUM_BITS = 16;
215     static constexpr size_t METHOD_ARGS_METHODID_BITS = 32;
216     static constexpr size_t METHOD_SLOT_SIZE_BITS = 16;
217     using HotnessCounterBits = BitField<int16_t, 0, METHOD_ARGS_NUM_BITS>; // offset 0-15
218     using MethodIdBits = HotnessCounterBits::NextField<uint32_t, METHOD_ARGS_METHODID_BITS>; // offset 16-47
219     using SlotSizeBits = MethodIdBits::NextField<uint16_t, METHOD_SLOT_SIZE_BITS>; // offset 48-63
220 
221     static constexpr size_t BUILTINID_NUM_BITS = 8;
222     static constexpr size_t FUNCTION_KIND_NUM_BITS = 4;
223     static constexpr size_t EMPTY_BITS = 16;
224     using BuiltinIdBits = BitField<uint8_t, 0, BUILTINID_NUM_BITS>; // offset 0-7
225     using FunctionKindBits = BuiltinIdBits::NextField<FunctionKind, FUNCTION_KIND_NUM_BITS>; // offset 8-11
226     using IsNoGCBit = FunctionKindBits::NextFlag; // offset 12
227     using HasDebuggerStmtBit = IsNoGCBit::NextFlag; // offset 13
228     using EmptyBit = HasDebuggerStmtBit::NextField<uint8_t, EMPTY_BITS>; // offset 14-29
229     using IsSharedBit = EmptyBit::NextFlag; // offset 30
230     using CanTypedCall = IsSharedBit::NextFlag; // offset 31
231 
SetHotnessCounterpanda::ecmascript::MethodLiteral232     inline NO_THREAD_SANITIZE void SetHotnessCounter(int16_t counter)
233     {
234         literalInfo_ = HotnessCounterBits::Update(literalInfo_, counter);
235     }
236 
GetMethodIdpanda::ecmascript::MethodLiteral237     EntityId GetMethodId() const
238     {
239         return EntityId(MethodIdBits::Decode(literalInfo_));
240     }
241 
GetSlotSizepanda::ecmascript::MethodLiteral242     uint32_t GetSlotSize() const
243     {
244         auto size = SlotSizeBits::Decode(literalInfo_);
245         return size == MAX_SLOT_SIZE ? MAX_SLOT_SIZE + EXTEND_SLOT_SIZE : size;
246     }
247 
UpdateSlotSizeWith8Bitpanda::ecmascript::MethodLiteral248     uint8_t UpdateSlotSizeWith8Bit(uint16_t size)
249     {
250         uint16_t start = SlotSizeBits::Decode(literalInfo_);
251         uint32_t end = start + size;
252         // ic overflow
253         if (end >= INVALID_IC_SLOT) {
254             if (start < INVALID_IC_SLOT + 1) {
255                 literalInfo_ = SlotSizeBits::Update(literalInfo_, INVALID_IC_SLOT + 1);
256             }
257             return INVALID_IC_SLOT;
258         }
259         literalInfo_ = SlotSizeBits::Update(literalInfo_, static_cast<uint8_t>(end));
260         return start;
261     }
262 
SetFunctionKindpanda::ecmascript::MethodLiteral263     void SetFunctionKind(FunctionKind kind)
264     {
265         extraLiteralInfo_ = FunctionKindBits::Update(extraLiteralInfo_, kind);
266     }
267 
SetNoGCBitpanda::ecmascript::MethodLiteral268     void SetNoGCBit(bool isNoGC)
269     {
270         extraLiteralInfo_ = IsNoGCBit::Update(extraLiteralInfo_, isNoGC);
271     }
272 
IsNoGCpanda::ecmascript::MethodLiteral273     bool IsNoGC() const
274     {
275         return IsNoGCBit::Decode(extraLiteralInfo_);
276     }
277 
SetHasDebuggerStmtBitpanda::ecmascript::MethodLiteral278     void SetHasDebuggerStmtBit(bool isDebuggerStmt)
279     {
280         extraLiteralInfo_ = HasDebuggerStmtBit::Update(extraLiteralInfo_, isDebuggerStmt);
281     }
282 
HasDebuggerStmtpanda::ecmascript::MethodLiteral283     bool HasDebuggerStmt() const
284     {
285         return HasDebuggerStmtBit::Decode(extraLiteralInfo_);
286     }
287 
SetIsSharedpanda::ecmascript::MethodLiteral288     void SetIsShared(bool isShared)
289     {
290         extraLiteralInfo_ = IsSharedBit::Update(extraLiteralInfo_, isShared);
291     }
292 
IsSharedpanda::ecmascript::MethodLiteral293     bool IsShared() const
294     {
295         return IsSharedBit::Decode(extraLiteralInfo_);
296     }
297 
SetCanTypedCallpanda::ecmascript::MethodLiteral298     void SetCanTypedCall(bool isTypedCall)
299     {
300         extraLiteralInfo_ = CanTypedCall::Update(extraLiteralInfo_, isTypedCall);
301     }
302 
IsTypedCallpanda::ecmascript::MethodLiteral303     bool IsTypedCall() const
304     {
305         return CanTypedCall::Decode(extraLiteralInfo_);
306     }
307 
GetFunctionKindpanda::ecmascript::MethodLiteral308     FunctionKind GetFunctionKind() const
309     {
310         return static_cast<FunctionKind>(FunctionKindBits::Decode(extraLiteralInfo_));
311     }
312 
IsClassConstructorpanda::ecmascript::MethodLiteral313     inline bool IsClassConstructor() const
314     {
315         return GetFunctionKind() == FunctionKind::CLASS_CONSTRUCTOR;
316     }
317 
GetHotnessCounterpanda::ecmascript::MethodLiteral318     static inline int16_t GetHotnessCounter(uint64_t literalInfo)
319     {
320         return HotnessCounterBits::Decode(literalInfo);
321     }
322 
SetHotnessCounterpanda::ecmascript::MethodLiteral323     static uint64_t SetHotnessCounter(uint64_t literalInfo, int16_t counter)
324     {
325         return HotnessCounterBits::Update(literalInfo, counter);
326     }
327 
SetFunctionKindpanda::ecmascript::MethodLiteral328     static uint64_t SetFunctionKind(uint64_t extraLiteralInfo, FunctionKind kind)
329     {
330         return FunctionKindBits::Update(extraLiteralInfo, kind);
331     }
332 
GetFunctionKindpanda::ecmascript::MethodLiteral333     static FunctionKind GetFunctionKind(uint64_t extraLiteralInfo)
334     {
335         return static_cast<FunctionKind>(FunctionKindBits::Decode(extraLiteralInfo));
336     }
337 
GetMethodIdpanda::ecmascript::MethodLiteral338     static EntityId GetMethodId(uint64_t literalInfo)
339     {
340         return EntityId(MethodIdBits::Decode(literalInfo));
341     }
342 
GetSlotSizepanda::ecmascript::MethodLiteral343     static uint32_t GetSlotSize(uint64_t literalInfo)
344     {
345         auto size = SlotSizeBits::Decode(literalInfo);
346         return size == MAX_SLOT_SIZE ? MAX_SLOT_SIZE + EXTEND_SLOT_SIZE : size;
347     }
348 
349     static const char PUBLIC_API *GetMethodName(const JSPandaFile *jsPandaFile, EntityId methodId,
350                                                 bool cpuProfiler = false);
351     static std::string PUBLIC_API ParseFunctionName(const JSPandaFile *jsPandaFile, EntityId methodId);
352     static std::pair<std::string_view, bool> PUBLIC_API ParseFunctionNameView(const JSPandaFile* jsPandaFile,
353                                                                               EntityId methodId);
354     static CString PUBLIC_API ParseFunctionNameToCString(const JSPandaFile *jsPandaFile, EntityId methodId);
355 
356     static uint32_t PUBLIC_API GetCodeSize(const JSPandaFile *jsPandaFile, EntityId methodId);
357     static CString PUBLIC_API GetRecordName(const JSPandaFile *jsPandaFile, EntityId methodId);
358     static const char PUBLIC_API *GetRecordNameWithSymbol(const JSPandaFile *jsPandaFile, EntityId methodId);
359 
GetBytecodeArraypanda::ecmascript::MethodLiteral360     const uint8_t *GetBytecodeArray() const
361     {
362         return reinterpret_cast<const uint8_t *>(nativePointerOrBytecodeArray_);
363     }
364 
GetNativePointerpanda::ecmascript::MethodLiteral365     const void* GetNativePointer() const
366     {
367         return nativePointerOrBytecodeArray_;
368     }
369 
GetLiteralInfopanda::ecmascript::MethodLiteral370     uint64_t GetLiteralInfo() const
371     {
372         return literalInfo_;
373     }
374 
GetExtraLiteralInfopanda::ecmascript::MethodLiteral375     uint64_t GetExtraLiteralInfo() const
376     {
377         return extraLiteralInfo_;
378     }
379 
380     std::optional<std::set<uint32_t>> GetConcurrentRequestedModules(const JSPandaFile *jsPandaFile) const;
381 
382 private:
383     enum class Index : size_t {
384         CALL_FIELD_INDEX = 0,
385         NATIVE_POINTER_OR_BYTECODE_ARRAY_INDEX,
386         LITERAL_INFO_INDEX,
387         EXTRA_LITERAL_INFO_INDEX,
388         NUM_OF_MEMBERS
389     };
390     static_assert(static_cast<size_t>(Index::NUM_OF_MEMBERS) == NumOfTypes);
391 
392     static std::pair<std::string_view, bool> GetMethodNameView(const JSPandaFile* jsPandaFile, EntityId methodId,
393                                                                bool cpuProfiler = false);
394 
SetMethodIdpanda::ecmascript::MethodLiteral395     void SetMethodId(EntityId methodId)
396     {
397         literalInfo_ = MethodIdBits::Update(literalInfo_, methodId.GetOffset());
398     }
399 
SetSlotSizepanda::ecmascript::MethodLiteral400     void SetSlotSize(uint32_t size)
401     {
402         if (size > MAX_SLOT_SIZE) {
403             size = MAX_SLOT_SIZE;
404         } else if (size + EXTEND_SLOT_SIZE > INVALID_IC_SLOT && size <= INVALID_IC_SLOT) {
405             // for compatibility: ensure there's always 0xff slot in this situation
406             size = INVALID_IC_SLOT + 1;
407         }
408         literalInfo_ = SlotSizeBits::Update(literalInfo_, size);
409     }
410 
411     alignas(EAS) uint64_t callField_ {0ULL};
412     // Native method decides this filed is NativePointer or BytecodeArray pointer.
413     alignas(EAS) const void *nativePointerOrBytecodeArray_ {nullptr};
414     // hotnessCounter, methodId and slotSize are encoded in literalInfo_.
415     alignas(EAS) uint64_t literalInfo_ {0ULL};
416     // BuiltinId, FunctionKind are encoded in extraLiteralInfo_.
417     alignas(EAS) uint64_t extraLiteralInfo_ {0ULL};
418 };
419 STATIC_ASSERT_EQ_ARCH(sizeof(MethodLiteral), MethodLiteral::SizeArch32, MethodLiteral::SizeArch64);
420 }  // namespace panda::ecmascript
421 
422 #endif  // ECMASCRIPT_JSPANDAFILE_METHOD_LITERAL_H
423