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