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
27static constexpr uint32_t CALL_TYPE_MASK = 0xF;  // 0xF: the last 4 bits are used as callType
28
29namespace panda::ecmascript {
30class JSPandaFile;
31using EntityId = panda_file::File::EntityId;
32using StringData = panda_file::File::StringData;
33struct PUBLIC_API MethodLiteral : public base::AlignedStruct<sizeof(uint64_t),
34                                                        base::AlignedUint64,
35                                                        base::AlignedPointer,
36                                                        base::AlignedUint64,
37                                                        base::AlignedUint64> {
38public:
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
62    uint64_t GetCallField() const
63    {
64        return callField_;
65    }
66
67    void SetNativeBit(bool isNative)
68    {
69        callField_ = IsNativeBit::Update(callField_, isNative);
70    }
71
72    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
79    bool HaveThisWithCallField() const
80    {
81        return HaveThisWithCallField(callField_);
82    }
83
84    bool HaveNewTargetWithCallField() const
85    {
86        return HaveNewTargetWithCallField(callField_);
87    }
88
89    bool HaveExtraWithCallField() const
90    {
91        return HaveExtraWithCallField(callField_);
92    }
93
94    bool HaveFuncWithCallField() const
95    {
96        return HaveFuncWithCallField(callField_);
97    }
98
99    bool IsNativeWithCallField() const
100    {
101        return IsNativeWithCallField(callField_);
102    }
103
104    uint32_t GetNumArgsWithCallField() const
105    {
106        return GetNumArgsWithCallField(callField_);
107    }
108
109    uint32_t GetNumArgs() const
110    {
111        return GetNumArgsWithCallField() + HaveFuncWithCallField() +
112            HaveNewTargetWithCallField() + HaveThisWithCallField();
113    }
114
115    uint32_t GetNumberVRegs() const
116    {
117        return GetNumVregsWithCallField() + GetNumArgs();
118    }
119
120    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
129    static uint64_t SetNativeBit(uint64_t callField, bool isNative)
130    {
131        return IsNativeBit::Update(callField, isNative);
132    }
133
134    static uint64_t SetAotCodeBit(uint64_t callField, bool isCompiled)
135    {
136        return IsAotCodeBit::Update(callField, isCompiled);
137    }
138
139    static bool HaveThisWithCallField(uint64_t callField)
140    {
141        return HaveThisBit::Decode(callField);
142    }
143
144    static bool HaveNewTargetWithCallField(uint64_t callField)
145    {
146        return HaveNewTargetBit::Decode(callField);
147    }
148
149    static bool HaveExtraWithCallField(uint64_t callField)
150    {
151        return HaveExtraBit::Decode(callField);
152    }
153
154    static bool HaveFuncWithCallField(uint64_t callField)
155    {
156        return HaveFuncBit::Decode(callField);
157    }
158
159    static bool IsNativeWithCallField(uint64_t callField)
160    {
161        return IsNativeBit::Decode(callField);
162    }
163
164    static bool IsAotWithCallField(uint64_t callField)
165    {
166        return IsAotCodeBit::Decode(callField);
167    }
168
169    static bool OnlyHaveThisWithCallField(uint64_t callField)
170    {
171        return (callField & CALL_TYPE_MASK) == 1;  // 1: the first bit of callFiled is HaveThisBit
172    }
173
174    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
179    static uint32_t GetNumVregsWithCallField(uint64_t callField)
180    {
181        return NumVregsBits::Decode(callField);
182    }
183
184    uint32_t GetNumVregsWithCallField() const
185    {
186        return NumVregsBits::Decode(callField_);
187    }
188
189    static uint32_t GetNumArgsWithCallField(uint64_t callField)
190    {
191        return NumArgsBits::Decode(callField);
192    }
193
194    static uint64_t SetIsFastCall(uint64_t callField, bool isFastCall)
195    {
196        return IsFastCallBit::Update(callField, isFastCall);
197    }
198
199    void SetIsFastCall(bool isFastCall)
200    {
201        callField_ = IsFastCallBit::Update(callField_, isFastCall);
202    }
203
204    static bool IsFastCall(uint64_t callField)
205    {
206        return IsFastCallBit::Decode(callField);
207    }
208
209    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
232    inline NO_THREAD_SANITIZE void SetHotnessCounter(int16_t counter)
233    {
234        literalInfo_ = HotnessCounterBits::Update(literalInfo_, counter);
235    }
236
237    EntityId GetMethodId() const
238    {
239        return EntityId(MethodIdBits::Decode(literalInfo_));
240    }
241
242    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
248    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
263    void SetFunctionKind(FunctionKind kind)
264    {
265        extraLiteralInfo_ = FunctionKindBits::Update(extraLiteralInfo_, kind);
266    }
267
268    void SetNoGCBit(bool isNoGC)
269    {
270        extraLiteralInfo_ = IsNoGCBit::Update(extraLiteralInfo_, isNoGC);
271    }
272
273    bool IsNoGC() const
274    {
275        return IsNoGCBit::Decode(extraLiteralInfo_);
276    }
277
278    void SetHasDebuggerStmtBit(bool isDebuggerStmt)
279    {
280        extraLiteralInfo_ = HasDebuggerStmtBit::Update(extraLiteralInfo_, isDebuggerStmt);
281    }
282
283    bool HasDebuggerStmt() const
284    {
285        return HasDebuggerStmtBit::Decode(extraLiteralInfo_);
286    }
287
288    void SetIsShared(bool isShared)
289    {
290        extraLiteralInfo_ = IsSharedBit::Update(extraLiteralInfo_, isShared);
291    }
292
293    bool IsShared() const
294    {
295        return IsSharedBit::Decode(extraLiteralInfo_);
296    }
297
298    void SetCanTypedCall(bool isTypedCall)
299    {
300        extraLiteralInfo_ = CanTypedCall::Update(extraLiteralInfo_, isTypedCall);
301    }
302
303    bool IsTypedCall() const
304    {
305        return CanTypedCall::Decode(extraLiteralInfo_);
306    }
307
308    FunctionKind GetFunctionKind() const
309    {
310        return static_cast<FunctionKind>(FunctionKindBits::Decode(extraLiteralInfo_));
311    }
312
313    inline bool IsClassConstructor() const
314    {
315        return GetFunctionKind() == FunctionKind::CLASS_CONSTRUCTOR;
316    }
317
318    static inline int16_t GetHotnessCounter(uint64_t literalInfo)
319    {
320        return HotnessCounterBits::Decode(literalInfo);
321    }
322
323    static uint64_t SetHotnessCounter(uint64_t literalInfo, int16_t counter)
324    {
325        return HotnessCounterBits::Update(literalInfo, counter);
326    }
327
328    static uint64_t SetFunctionKind(uint64_t extraLiteralInfo, FunctionKind kind)
329    {
330        return FunctionKindBits::Update(extraLiteralInfo, kind);
331    }
332
333    static FunctionKind GetFunctionKind(uint64_t extraLiteralInfo)
334    {
335        return static_cast<FunctionKind>(FunctionKindBits::Decode(extraLiteralInfo));
336    }
337
338    static EntityId GetMethodId(uint64_t literalInfo)
339    {
340        return EntityId(MethodIdBits::Decode(literalInfo));
341    }
342
343    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
360    const uint8_t *GetBytecodeArray() const
361    {
362        return reinterpret_cast<const uint8_t *>(nativePointerOrBytecodeArray_);
363    }
364
365    const void* GetNativePointer() const
366    {
367        return nativePointerOrBytecodeArray_;
368    }
369
370    uint64_t GetLiteralInfo() const
371    {
372        return literalInfo_;
373    }
374
375    uint64_t GetExtraLiteralInfo() const
376    {
377        return extraLiteralInfo_;
378    }
379
380    std::optional<std::set<uint32_t>> GetConcurrentRequestedModules(const JSPandaFile *jsPandaFile) const;
381
382private:
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
395    void SetMethodId(EntityId methodId)
396    {
397        literalInfo_ = MethodIdBits::Update(literalInfo_, methodId.GetOffset());
398    }
399
400    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};
419STATIC_ASSERT_EQ_ARCH(sizeof(MethodLiteral), MethodLiteral::SizeArch32, MethodLiteral::SizeArch64);
420}  // namespace panda::ecmascript
421
422#endif  // ECMASCRIPT_JSPANDAFILE_METHOD_LITERAL_H
423