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 GetCallFieldMethodLiteral62 uint64_t GetCallField() const 63 { 64 return callField_; 65 } 66 SetNativeBitMethodLiteral67 void SetNativeBit(bool isNative) 68 { 69 callField_ = IsNativeBit::Update(callField_, isNative); 70 } 71 SetAotCodeBitMethodLiteral72 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 const uint32_t offset = 0); 79 HaveThisWithCallFieldMethodLiteral80 bool HaveThisWithCallField() const 81 { 82 return HaveThisWithCallField(callField_); 83 } 84 HaveNewTargetWithCallFieldMethodLiteral85 bool HaveNewTargetWithCallField() const 86 { 87 return HaveNewTargetWithCallField(callField_); 88 } 89 HaveExtraWithCallFieldMethodLiteral90 bool HaveExtraWithCallField() const 91 { 92 return HaveExtraWithCallField(callField_); 93 } 94 HaveFuncWithCallFieldMethodLiteral95 bool HaveFuncWithCallField() const 96 { 97 return HaveFuncWithCallField(callField_); 98 } 99 IsNativeWithCallFieldMethodLiteral100 bool IsNativeWithCallField() const 101 { 102 return IsNativeWithCallField(callField_); 103 } 104 GetNumArgsWithCallFieldMethodLiteral105 uint32_t GetNumArgsWithCallField() const 106 { 107 return GetNumArgsWithCallField(callField_); 108 } 109 GetNumArgsMethodLiteral110 uint32_t GetNumArgs() const 111 { 112 return GetNumArgsWithCallField() + HaveFuncWithCallField() + 113 HaveNewTargetWithCallField() + HaveThisWithCallField(); 114 } 115 GetNumberVRegsMethodLiteral116 uint32_t GetNumberVRegs() const 117 { 118 return GetNumVregsWithCallField() + GetNumArgs(); 119 } 120 GetNewTargetVregIndexMethodLiteral121 uint32_t GetNewTargetVregIndex() const 122 { 123 if (!HaveNewTargetWithCallField()) { 124 return 0; 125 } 126 uint32_t numVregs = GetNumVregsWithCallField(); 127 return HaveFuncWithCallField() ? (numVregs + 1) : numVregs; 128 } 129 SetNativeBitMethodLiteral130 static uint64_t SetNativeBit(uint64_t callField, bool isNative) 131 { 132 return IsNativeBit::Update(callField, isNative); 133 } 134 SetAotCodeBitMethodLiteral135 static uint64_t SetAotCodeBit(uint64_t callField, bool isCompiled) 136 { 137 return IsAotCodeBit::Update(callField, isCompiled); 138 } 139 HaveThisWithCallFieldMethodLiteral140 static bool HaveThisWithCallField(uint64_t callField) 141 { 142 return HaveThisBit::Decode(callField); 143 } 144 HaveNewTargetWithCallFieldMethodLiteral145 static bool HaveNewTargetWithCallField(uint64_t callField) 146 { 147 return HaveNewTargetBit::Decode(callField); 148 } 149 HaveExtraWithCallFieldMethodLiteral150 static bool HaveExtraWithCallField(uint64_t callField) 151 { 152 return HaveExtraBit::Decode(callField); 153 } 154 HaveFuncWithCallFieldMethodLiteral155 static bool HaveFuncWithCallField(uint64_t callField) 156 { 157 return HaveFuncBit::Decode(callField); 158 } 159 IsNativeWithCallFieldMethodLiteral160 static bool IsNativeWithCallField(uint64_t callField) 161 { 162 return IsNativeBit::Decode(callField); 163 } 164 IsAotWithCallFieldMethodLiteral165 static bool IsAotWithCallField(uint64_t callField) 166 { 167 return IsAotCodeBit::Decode(callField); 168 } 169 OnlyHaveThisWithCallFieldMethodLiteral170 static bool OnlyHaveThisWithCallField(uint64_t callField) 171 { 172 return (callField & CALL_TYPE_MASK) == 1; // 1: the first bit of callFiled is HaveThisBit 173 } 174 OnlyHaveNewTagetAndThisWithCallFieldMethodLiteral175 static bool OnlyHaveNewTagetAndThisWithCallField(uint64_t callField) 176 { 177 return (callField & CALL_TYPE_MASK) == 0b11; // the first two bit of callFiled is `This` and `NewTarget` 178 } 179 GetNumVregsWithCallFieldMethodLiteral180 static uint32_t GetNumVregsWithCallField(uint64_t callField) 181 { 182 return NumVregsBits::Decode(callField); 183 } 184 GetNumVregsWithCallFieldMethodLiteral185 uint32_t GetNumVregsWithCallField() const 186 { 187 return NumVregsBits::Decode(callField_); 188 } 189 GetNumArgsWithCallFieldMethodLiteral190 static uint32_t GetNumArgsWithCallField(uint64_t callField) 191 { 192 return NumArgsBits::Decode(callField); 193 } 194 SetIsFastCallMethodLiteral195 static uint64_t SetIsFastCall(uint64_t callField, bool isFastCall) 196 { 197 return IsFastCallBit::Update(callField, isFastCall); 198 } 199 SetIsFastCallMethodLiteral200 void SetIsFastCall(bool isFastCall) 201 { 202 callField_ = IsFastCallBit::Update(callField_, isFastCall); 203 } 204 IsFastCallMethodLiteral205 static bool IsFastCall(uint64_t callField) 206 { 207 return IsFastCallBit::Decode(callField); 208 } 209 IsFastCallMethodLiteral210 bool IsFastCall() const 211 { 212 return IsFastCallBit::Decode(callField_); 213 } 214 215 static constexpr size_t METHOD_ARGS_NUM_BITS = 16; 216 static constexpr size_t METHOD_ARGS_METHODID_BITS = 32; 217 static constexpr size_t METHOD_SLOT_SIZE_BITS = 16; 218 using HotnessCounterBits = BitField<int16_t, 0, METHOD_ARGS_NUM_BITS>; // offset 0-15 219 using MethodIdBits = HotnessCounterBits::NextField<uint32_t, METHOD_ARGS_METHODID_BITS>; // offset 16-47 220 using SlotSizeBits = MethodIdBits::NextField<uint16_t, METHOD_SLOT_SIZE_BITS>; // offset 48-63 221 222 static constexpr size_t BUILTINID_NUM_BITS = 8; 223 static constexpr size_t FUNCTION_KIND_NUM_BITS = 4; 224 static constexpr size_t EMPTY_BITS = 16; 225 using BuiltinIdBits = BitField<uint8_t, 0, BUILTINID_NUM_BITS>; // offset 0-7 226 using FunctionKindBits = BuiltinIdBits::NextField<FunctionKind, FUNCTION_KIND_NUM_BITS>; // offset 8-11 227 using IsNoGCBit = FunctionKindBits::NextFlag; // offset 12 228 using HasDebuggerStmtBit = IsNoGCBit::NextFlag; // offset 13 229 using EmptyBit = HasDebuggerStmtBit::NextField<uint8_t, EMPTY_BITS>; // offset 14-29 230 using IsSharedBit = EmptyBit::NextFlag; // offset 30 231 using CanTypedCall = IsSharedBit::NextFlag; // offset 31 232 SetHotnessCounterMethodLiteral233 inline NO_THREAD_SANITIZE void SetHotnessCounter(int16_t counter) 234 { 235 literalInfo_ = HotnessCounterBits::Update(literalInfo_, counter); 236 } 237 GetMethodIdMethodLiteral238 EntityId GetMethodId() const 239 { 240 return EntityId(MethodIdBits::Decode(literalInfo_)); 241 } 242 GetSlotSizeMethodLiteral243 uint32_t GetSlotSize() const 244 { 245 auto size = SlotSizeBits::Decode(literalInfo_); 246 return size == MAX_SLOT_SIZE ? MAX_SLOT_SIZE + EXTEND_SLOT_SIZE : size; 247 } 248 UpdateSlotSizeWith8BitMethodLiteral249 uint8_t UpdateSlotSizeWith8Bit(uint16_t size) 250 { 251 uint16_t start = SlotSizeBits::Decode(literalInfo_); 252 uint32_t end = start + size; 253 // ic overflow 254 if (end >= INVALID_IC_SLOT) { 255 if (start < INVALID_IC_SLOT + 1) { 256 literalInfo_ = SlotSizeBits::Update(literalInfo_, INVALID_IC_SLOT + 1); 257 } 258 return INVALID_IC_SLOT; 259 } 260 literalInfo_ = SlotSizeBits::Update(literalInfo_, static_cast<uint8_t>(end)); 261 return start; 262 } 263 SetFunctionKindMethodLiteral264 void SetFunctionKind(FunctionKind kind) 265 { 266 extraLiteralInfo_ = FunctionKindBits::Update(extraLiteralInfo_, kind); 267 } 268 SetNoGCBitMethodLiteral269 void SetNoGCBit(bool isNoGC) 270 { 271 extraLiteralInfo_ = IsNoGCBit::Update(extraLiteralInfo_, isNoGC); 272 } 273 IsNoGCMethodLiteral274 bool IsNoGC() const 275 { 276 return IsNoGCBit::Decode(extraLiteralInfo_); 277 } 278 SetHasDebuggerStmtBitMethodLiteral279 void SetHasDebuggerStmtBit(bool isDebuggerStmt) 280 { 281 extraLiteralInfo_ = HasDebuggerStmtBit::Update(extraLiteralInfo_, isDebuggerStmt); 282 } 283 HasDebuggerStmtMethodLiteral284 bool HasDebuggerStmt() const 285 { 286 return HasDebuggerStmtBit::Decode(extraLiteralInfo_); 287 } 288 SetIsSharedMethodLiteral289 void SetIsShared(bool isShared) 290 { 291 extraLiteralInfo_ = IsSharedBit::Update(extraLiteralInfo_, isShared); 292 } 293 IsSharedMethodLiteral294 bool IsShared() const 295 { 296 return IsSharedBit::Decode(extraLiteralInfo_); 297 } 298 SetCanTypedCallMethodLiteral299 void SetCanTypedCall(bool isTypedCall) 300 { 301 extraLiteralInfo_ = CanTypedCall::Update(extraLiteralInfo_, isTypedCall); 302 } 303 IsTypedCallMethodLiteral304 bool IsTypedCall() const 305 { 306 return CanTypedCall::Decode(extraLiteralInfo_); 307 } 308 GetFunctionKindMethodLiteral309 FunctionKind GetFunctionKind() const 310 { 311 return static_cast<FunctionKind>(FunctionKindBits::Decode(extraLiteralInfo_)); 312 } 313 IsClassConstructorMethodLiteral314 inline bool IsClassConstructor() const 315 { 316 return GetFunctionKind() == FunctionKind::CLASS_CONSTRUCTOR; 317 } 318 GetHotnessCounterMethodLiteral319 static inline int16_t GetHotnessCounter(uint64_t literalInfo) 320 { 321 return HotnessCounterBits::Decode(literalInfo); 322 } 323 SetHotnessCounterMethodLiteral324 static uint64_t SetHotnessCounter(uint64_t literalInfo, int16_t counter) 325 { 326 return HotnessCounterBits::Update(literalInfo, counter); 327 } 328 SetFunctionKindMethodLiteral329 static uint64_t SetFunctionKind(uint64_t extraLiteralInfo, FunctionKind kind) 330 { 331 return FunctionKindBits::Update(extraLiteralInfo, kind); 332 } 333 GetFunctionKindMethodLiteral334 static FunctionKind GetFunctionKind(uint64_t extraLiteralInfo) 335 { 336 return static_cast<FunctionKind>(FunctionKindBits::Decode(extraLiteralInfo)); 337 } 338 GetMethodIdMethodLiteral339 static EntityId GetMethodId(uint64_t literalInfo) 340 { 341 return EntityId(MethodIdBits::Decode(literalInfo)); 342 } 343 GetSlotSizeMethodLiteral344 static uint32_t GetSlotSize(uint64_t literalInfo) 345 { 346 auto size = SlotSizeBits::Decode(literalInfo); 347 return size == MAX_SLOT_SIZE ? MAX_SLOT_SIZE + EXTEND_SLOT_SIZE : size; 348 } 349 350 static const char PUBLIC_API *GetMethodName(const JSPandaFile *jsPandaFile, EntityId methodId, 351 bool cpuProfiler = false); 352 static std::string PUBLIC_API ParseFunctionName(const JSPandaFile *jsPandaFile, EntityId methodId); 353 static std::pair<std::string_view, bool> PUBLIC_API ParseFunctionNameView(const JSPandaFile* jsPandaFile, 354 EntityId methodId); 355 static CString PUBLIC_API ParseFunctionNameToCString(const JSPandaFile *jsPandaFile, EntityId methodId); 356 357 static uint32_t PUBLIC_API GetCodeSize(const JSPandaFile *jsPandaFile, EntityId methodId); 358 static CString PUBLIC_API GetRecordName(const JSPandaFile *jsPandaFile, EntityId methodId); 359 static const char PUBLIC_API *GetRecordNameWithSymbol(const JSPandaFile *jsPandaFile, EntityId methodId); 360 GetBytecodeArrayMethodLiteral361 const uint8_t *GetBytecodeArray() const 362 { 363 return reinterpret_cast<const uint8_t *>(nativePointerOrBytecodeArray_); 364 } 365 GetNativePointerMethodLiteral366 const void* GetNativePointer() const 367 { 368 return nativePointerOrBytecodeArray_; 369 } 370 GetLiteralInfoMethodLiteral371 uint64_t GetLiteralInfo() const 372 { 373 return literalInfo_; 374 } 375 GetExtraLiteralInfoMethodLiteral376 uint64_t GetExtraLiteralInfo() const 377 { 378 return extraLiteralInfo_; 379 } 380 381 std::optional<std::set<uint32_t>> GetConcurrentRequestedModules(const JSPandaFile *jsPandaFile) const; 382 383 private: 384 enum class Index : size_t { 385 CALL_FIELD_INDEX = 0, 386 NATIVE_POINTER_OR_BYTECODE_ARRAY_INDEX, 387 LITERAL_INFO_INDEX, 388 EXTRA_LITERAL_INFO_INDEX, 389 NUM_OF_MEMBERS 390 }; 391 static_assert(static_cast<size_t>(Index::NUM_OF_MEMBERS) == NumOfTypes); 392 393 static std::pair<std::string_view, bool> GetMethodNameView(const JSPandaFile* jsPandaFile, EntityId methodId, 394 bool cpuProfiler = false); 395 SetMethodIdMethodLiteral396 void SetMethodId(EntityId methodId) 397 { 398 literalInfo_ = MethodIdBits::Update(literalInfo_, methodId.GetOffset()); 399 } 400 SetSlotSizeMethodLiteral401 void SetSlotSize(uint32_t size) 402 { 403 if (size > MAX_SLOT_SIZE) { 404 size = MAX_SLOT_SIZE; 405 } else if (size + EXTEND_SLOT_SIZE > INVALID_IC_SLOT && size <= INVALID_IC_SLOT) { 406 // for compatibility: ensure there's always 0xff slot in this situation 407 size = INVALID_IC_SLOT + 1; 408 } 409 literalInfo_ = SlotSizeBits::Update(literalInfo_, size); 410 } 411 412 alignas(EAS) uint64_t callField_ {0ULL}; 413 // Native method decides this filed is NativePointer or BytecodeArray pointer. 414 alignas(EAS) const void *nativePointerOrBytecodeArray_ {nullptr}; 415 // hotnessCounter, methodId and slotSize are encoded in literalInfo_. 416 alignas(EAS) uint64_t literalInfo_ {0ULL}; 417 // BuiltinId, FunctionKind are encoded in extraLiteralInfo_. 418 alignas(EAS) uint64_t extraLiteralInfo_ {0ULL}; 419 }; 420 STATIC_ASSERT_EQ_ARCH(sizeof(MethodLiteral), MethodLiteral::SizeArch32, MethodLiteral::SizeArch64); 421 } // namespace panda::ecmascript 422 423 #endif // ECMASCRIPT_JSPANDAFILE_METHOD_LITERAL_H 424