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/compiler/builtins/builtins_call_signature.h" 23 #include "ecmascript/js_function_kind.h" 24 #include "ecmascript/js_tagged_value.h" 25 #include "ecmascript/mem/c_string.h" 26 #include "libpandafile/file.h" 27 28 static constexpr uint32_t CALL_TYPE_MASK = 0xF; // 0xF: the last 4 bits are used as callType 29 30 namespace panda::ecmascript { 31 class JSPandaFile; 32 using EntityId = panda_file::File::EntityId; 33 using StringData = panda_file::File::StringData; 34 struct PUBLIC_API MethodLiteral : public base::AlignedStruct<sizeof(uint64_t), 35 base::AlignedUint64, 36 base::AlignedPointer, 37 base::AlignedUint64, 38 base::AlignedUint64> { 39 public: 40 static constexpr uint8_t INVALID_IC_SLOT = 0xFFU; 41 static constexpr uint16_t MAX_SLOT_SIZE = 0xFFFFU; 42 static constexpr uint32_t MAX_EXPECTED_PROPERTY_COUNT = 0xFFFFFFFFU; 43 static constexpr size_t EXTEND_SLOT_SIZE = 2; 44 static constexpr std::string_view KCALL_TYPE_ANNOTATION = "L_ESCallTypeAnnotation;"; 45 static constexpr std::string_view KCALL_TYPE_NAME = "callType"; 46 static constexpr std::string_view KSLOT_NUMBER_ANNOTATION = "L_ESSlotNumberAnnotation;"; 47 static constexpr std::string_view KSLOT_NUMBER_NAME = "SlotNumber"; 48 static constexpr std::string_view KEXPECTED_PROPERTY_COUNT_ANNOTATION = "L_ESExpectedPropertyCountAnnotation;"; 49 static constexpr std::string_view KEXPECTED_PROPERTY_COUNT_NAME = "ExpectedPropertyCount"; 50 51 PUBLIC_API explicit MethodLiteral(EntityId methodId); 52 MethodLiteral() = delete; 53 ~MethodLiteral() = default; 54 55 NO_COPY_SEMANTIC(MethodLiteral); 56 NO_MOVE_SEMANTIC(MethodLiteral); 57 58 static constexpr size_t VREGS_ARGS_NUM_BITS = 28; // 28: maximum 268,435,455 59 using HaveThisBit = BitField<bool, 0, 1>; // offset 0 60 using HaveNewTargetBit = HaveThisBit::NextFlag; // offset 1 61 using HaveExtraBit = HaveNewTargetBit::NextFlag; // offset 2 62 using HaveFuncBit = HaveExtraBit::NextFlag; // offset 3 63 using NumVregsBits = HaveFuncBit::NextField<uint32_t, VREGS_ARGS_NUM_BITS>; // offset 4-31 64 using NumArgsBits = NumVregsBits::NextField<uint32_t, VREGS_ARGS_NUM_BITS>; // offset 32-59 65 using IsNativeBit = NumArgsBits::NextFlag; // offset 60 66 using IsAotCodeBit = IsNativeBit::NextFlag; // offset 61 67 using IsFastBuiltinBit = IsAotCodeBit::NextFlag; // offset 62 68 using IsFastCallBit = IsFastBuiltinBit::NextFlag; // offset 63 69 GetCallFieldMethodLiteral70 uint64_t GetCallField() const 71 { 72 return callField_; 73 } 74 SetNativeBitMethodLiteral75 void SetNativeBit(bool isNative) 76 { 77 callField_ = IsNativeBit::Update(callField_, isNative); 78 } 79 SetAotCodeBitMethodLiteral80 void SetAotCodeBit(bool isCompiled) 81 { 82 callField_ = IsAotCodeBit::Update(callField_, isCompiled); 83 } 84 85 void PUBLIC_API Initialize(const JSPandaFile *jsPandaFile, const JSThread *thread = nullptr, 86 const uint32_t offset = 0); 87 HaveThisWithCallFieldMethodLiteral88 bool HaveThisWithCallField() const 89 { 90 return HaveThisWithCallField(callField_); 91 } 92 HaveNewTargetWithCallFieldMethodLiteral93 bool HaveNewTargetWithCallField() const 94 { 95 return HaveNewTargetWithCallField(callField_); 96 } 97 HaveExtraWithCallFieldMethodLiteral98 bool HaveExtraWithCallField() const 99 { 100 return HaveExtraWithCallField(callField_); 101 } 102 HaveFuncWithCallFieldMethodLiteral103 bool HaveFuncWithCallField() const 104 { 105 return HaveFuncWithCallField(callField_); 106 } 107 IsNativeWithCallFieldMethodLiteral108 bool IsNativeWithCallField() const 109 { 110 return IsNativeWithCallField(callField_); 111 } 112 GetNumArgsWithCallFieldMethodLiteral113 uint32_t GetNumArgsWithCallField() const 114 { 115 return GetNumArgsWithCallField(callField_); 116 } 117 GetNumArgsMethodLiteral118 uint32_t GetNumArgs() const 119 { 120 return GetNumArgsWithCallField() + HaveFuncWithCallField() + 121 HaveNewTargetWithCallField() + HaveThisWithCallField(); 122 } 123 GetNumberVRegsMethodLiteral124 uint32_t GetNumberVRegs() const 125 { 126 return GetNumVregsWithCallField() + GetNumArgs(); 127 } 128 GetNewTargetVregIndexMethodLiteral129 uint32_t GetNewTargetVregIndex() const 130 { 131 if (!HaveNewTargetWithCallField()) { 132 return 0; 133 } 134 uint32_t numVregs = GetNumVregsWithCallField(); 135 return HaveFuncWithCallField() ? (numVregs + 1) : numVregs; 136 } 137 SetNativeBitMethodLiteral138 static uint64_t SetNativeBit(uint64_t callField, bool isNative) 139 { 140 return IsNativeBit::Update(callField, isNative); 141 } 142 SetAotCodeBitMethodLiteral143 static uint64_t SetAotCodeBit(uint64_t callField, bool isCompiled) 144 { 145 return IsAotCodeBit::Update(callField, isCompiled); 146 } 147 HaveThisWithCallFieldMethodLiteral148 static bool HaveThisWithCallField(uint64_t callField) 149 { 150 return HaveThisBit::Decode(callField); 151 } 152 HaveNewTargetWithCallFieldMethodLiteral153 static bool HaveNewTargetWithCallField(uint64_t callField) 154 { 155 return HaveNewTargetBit::Decode(callField); 156 } 157 HaveExtraWithCallFieldMethodLiteral158 static bool HaveExtraWithCallField(uint64_t callField) 159 { 160 return HaveExtraBit::Decode(callField); 161 } 162 HaveFuncWithCallFieldMethodLiteral163 static bool HaveFuncWithCallField(uint64_t callField) 164 { 165 return HaveFuncBit::Decode(callField); 166 } 167 IsNativeWithCallFieldMethodLiteral168 static bool IsNativeWithCallField(uint64_t callField) 169 { 170 return IsNativeBit::Decode(callField); 171 } 172 IsAotWithCallFieldMethodLiteral173 static bool IsAotWithCallField(uint64_t callField) 174 { 175 return IsAotCodeBit::Decode(callField); 176 } 177 OnlyHaveThisWithCallFieldMethodLiteral178 static bool OnlyHaveThisWithCallField(uint64_t callField) 179 { 180 return (callField & CALL_TYPE_MASK) == 1; // 1: the first bit of callFiled is HaveThisBit 181 } 182 OnlyHaveNewTagetAndThisWithCallFieldMethodLiteral183 static bool OnlyHaveNewTagetAndThisWithCallField(uint64_t callField) 184 { 185 return (callField & CALL_TYPE_MASK) == 0b11; // the first two bit of callFiled is `This` and `NewTarget` 186 } 187 GetNumVregsWithCallFieldMethodLiteral188 static uint32_t GetNumVregsWithCallField(uint64_t callField) 189 { 190 return NumVregsBits::Decode(callField); 191 } 192 GetNumVregsWithCallFieldMethodLiteral193 uint32_t GetNumVregsWithCallField() const 194 { 195 return NumVregsBits::Decode(callField_); 196 } 197 GetNumArgsWithCallFieldMethodLiteral198 static uint32_t GetNumArgsWithCallField(uint64_t callField) 199 { 200 return NumArgsBits::Decode(callField); 201 } 202 SetIsFastCallMethodLiteral203 static uint64_t SetIsFastCall(uint64_t callField, bool isFastCall) 204 { 205 return IsFastCallBit::Update(callField, isFastCall); 206 } 207 SetIsFastCallMethodLiteral208 void SetIsFastCall(bool isFastCall) 209 { 210 callField_ = IsFastCallBit::Update(callField_, isFastCall); 211 } 212 IsFastCallMethodLiteral213 static bool IsFastCall(uint64_t callField) 214 { 215 return IsFastCallBit::Decode(callField); 216 } 217 IsFastCallMethodLiteral218 bool IsFastCall() const 219 { 220 return IsFastCallBit::Decode(callField_); 221 } 222 223 static constexpr size_t METHOD_ARGS_NUM_BITS = 16; 224 static constexpr size_t METHOD_ARGS_METHODID_BITS = 32; 225 static constexpr size_t METHOD_EXPECTED_PROPERTY_COUNT_BITS = 32; 226 static constexpr size_t METHOD_SLOT_SIZE_BITS = 16; 227 using HotnessCounterBits = BitField<int16_t, 0, METHOD_ARGS_NUM_BITS>; // offset 0-15 228 using MethodIdBits = HotnessCounterBits::NextField<uint32_t, METHOD_ARGS_METHODID_BITS>; // offset 16-47 229 using SlotSizeBits = MethodIdBits::NextField<uint16_t, METHOD_SLOT_SIZE_BITS>; // offset 48-63 230 231 static constexpr size_t BUILTINID_NUM_BITS = 8; 232 static constexpr size_t FUNCTION_KIND_NUM_BITS = 4; 233 static constexpr size_t EMPTY_BITS = 16; 234 using BuiltinIdBits = BitField<uint8_t, 0, BUILTINID_NUM_BITS>; // offset 0-7 235 using FunctionKindBits = BuiltinIdBits::NextField<FunctionKind, FUNCTION_KIND_NUM_BITS>; // offset 8-11 236 using IsNoGCBit = FunctionKindBits::NextFlag; // offset 12 237 using HasDebuggerStmtBit = IsNoGCBit::NextFlag; // offset 13 238 using EmptyBit = HasDebuggerStmtBit::NextField<uint8_t, EMPTY_BITS>; // offset 14-29 239 using IsSharedBit = EmptyBit::NextFlag; // offset 30 240 using CanTypedCall = IsSharedBit::NextFlag; // offset 31 241 using ExpectedPropertyCountBits = 242 CanTypedCall::NextField<uint32_t, METHOD_EXPECTED_PROPERTY_COUNT_BITS>; // offset 32-63 243 244 // one placeholder 0xffff (INVALID) in kungfu::BuiltinsStubCSigns::ID 245 static_assert(static_cast<size_t>(kungfu::BuiltinsStubCSigns::ID::NUM_OF_BUILTINS_ID) < (1 << BUILTINID_NUM_BITS)); 246 static_assert(static_cast<size_t>(FunctionKind::LAST_FUNCTION_KIND) <= (1 << FUNCTION_KIND_NUM_BITS)); 247 SetHotnessCounterMethodLiteral248 inline NO_THREAD_SANITIZE void SetHotnessCounter(int16_t counter) 249 { 250 literalInfo_ = HotnessCounterBits::Update(literalInfo_, counter); 251 } 252 GetMethodIdMethodLiteral253 EntityId GetMethodId() const 254 { 255 return EntityId(MethodIdBits::Decode(literalInfo_)); 256 } 257 GetSlotSizeMethodLiteral258 uint32_t GetSlotSize() const 259 { 260 auto size = SlotSizeBits::Decode(literalInfo_); 261 return size == MAX_SLOT_SIZE ? MAX_SLOT_SIZE + EXTEND_SLOT_SIZE : size; 262 } 263 UpdateSlotSizeWith8BitMethodLiteral264 uint8_t UpdateSlotSizeWith8Bit(uint16_t size) 265 { 266 uint16_t start = SlotSizeBits::Decode(literalInfo_); 267 uint32_t end = start + size; 268 // ic overflow 269 if (end >= INVALID_IC_SLOT) { 270 if (start < INVALID_IC_SLOT + 1) { 271 literalInfo_ = SlotSizeBits::Update(literalInfo_, INVALID_IC_SLOT + 1); 272 } 273 return INVALID_IC_SLOT; 274 } 275 literalInfo_ = SlotSizeBits::Update(literalInfo_, static_cast<uint8_t>(end)); 276 return start; 277 } 278 SetFunctionKindMethodLiteral279 void SetFunctionKind(FunctionKind kind) 280 { 281 extraLiteralInfo_ = FunctionKindBits::Update(extraLiteralInfo_, kind); 282 } 283 SetNoGCBitMethodLiteral284 void SetNoGCBit(bool isNoGC) 285 { 286 extraLiteralInfo_ = IsNoGCBit::Update(extraLiteralInfo_, isNoGC); 287 } 288 IsNoGCMethodLiteral289 bool IsNoGC() const 290 { 291 return IsNoGCBit::Decode(extraLiteralInfo_); 292 } 293 SetHasDebuggerStmtBitMethodLiteral294 void SetHasDebuggerStmtBit(bool isDebuggerStmt) 295 { 296 extraLiteralInfo_ = HasDebuggerStmtBit::Update(extraLiteralInfo_, isDebuggerStmt); 297 } 298 HasDebuggerStmtMethodLiteral299 bool HasDebuggerStmt() const 300 { 301 return HasDebuggerStmtBit::Decode(extraLiteralInfo_); 302 } 303 SetIsSharedMethodLiteral304 void SetIsShared(bool isShared) 305 { 306 extraLiteralInfo_ = IsSharedBit::Update(extraLiteralInfo_, isShared); 307 } 308 IsSharedMethodLiteral309 bool IsShared() const 310 { 311 return IsSharedBit::Decode(extraLiteralInfo_); 312 } 313 SetCanTypedCallMethodLiteral314 void SetCanTypedCall(bool isTypedCall) 315 { 316 extraLiteralInfo_ = CanTypedCall::Update(extraLiteralInfo_, isTypedCall); 317 } 318 IsTypedCallMethodLiteral319 bool IsTypedCall() const 320 { 321 return CanTypedCall::Decode(extraLiteralInfo_); 322 } 323 GetFunctionKindMethodLiteral324 FunctionKind GetFunctionKind() const 325 { 326 return static_cast<FunctionKind>(FunctionKindBits::Decode(extraLiteralInfo_)); 327 } 328 GetExpectedPropertyCountMethodLiteral329 uint32_t GetExpectedPropertyCount() const 330 { 331 return ExpectedPropertyCountBits::Decode(extraLiteralInfo_); 332 } 333 SetExpectedPropertyCountMethodLiteral334 void SetExpectedPropertyCount(uint32_t count) 335 { 336 extraLiteralInfo_ = ExpectedPropertyCountBits::Update(extraLiteralInfo_, count); 337 } 338 IsClassConstructorMethodLiteral339 inline bool IsClassConstructor() const 340 { 341 return GetFunctionKind() == FunctionKind::CLASS_CONSTRUCTOR; 342 } 343 GetHotnessCounterMethodLiteral344 static inline int16_t GetHotnessCounter(uint64_t literalInfo) 345 { 346 return HotnessCounterBits::Decode(literalInfo); 347 } 348 SetHotnessCounterMethodLiteral349 static uint64_t SetHotnessCounter(uint64_t literalInfo, int16_t counter) 350 { 351 return HotnessCounterBits::Update(literalInfo, counter); 352 } 353 SetFunctionKindMethodLiteral354 static uint64_t SetFunctionKind(uint64_t extraLiteralInfo, FunctionKind kind) 355 { 356 return FunctionKindBits::Update(extraLiteralInfo, kind); 357 } 358 GetFunctionKindMethodLiteral359 static FunctionKind GetFunctionKind(uint64_t extraLiteralInfo) 360 { 361 return static_cast<FunctionKind>(FunctionKindBits::Decode(extraLiteralInfo)); 362 } 363 GetMethodIdMethodLiteral364 static EntityId GetMethodId(uint64_t literalInfo) 365 { 366 return EntityId(MethodIdBits::Decode(literalInfo)); 367 } 368 GetSlotSizeMethodLiteral369 static uint32_t GetSlotSize(uint64_t literalInfo) 370 { 371 auto size = SlotSizeBits::Decode(literalInfo); 372 return size == MAX_SLOT_SIZE ? MAX_SLOT_SIZE + EXTEND_SLOT_SIZE : size; 373 } 374 375 static const char PUBLIC_API *GetMethodName(const JSPandaFile *jsPandaFile, EntityId methodId, 376 bool cpuProfiler = false); 377 static std::string PUBLIC_API ParseFunctionName(const JSPandaFile *jsPandaFile, EntityId methodId); 378 static std::pair<std::string_view, bool> PUBLIC_API ParseFunctionNameView(const JSPandaFile* jsPandaFile, 379 EntityId methodId); 380 static CString PUBLIC_API ParseFunctionNameToCString(const JSPandaFile *jsPandaFile, EntityId methodId); 381 382 static uint32_t PUBLIC_API GetCodeSize(const JSPandaFile *jsPandaFile, EntityId methodId); 383 static CString PUBLIC_API GetRecordName(const JSPandaFile *jsPandaFile, EntityId methodId); 384 static const char PUBLIC_API *GetRecordNameWithSymbol(const JSPandaFile *jsPandaFile, EntityId methodId); 385 GetBytecodeArrayMethodLiteral386 const uint8_t *GetBytecodeArray() const 387 { 388 return reinterpret_cast<const uint8_t *>(nativePointerOrBytecodeArray_); 389 } 390 GetNativePointerMethodLiteral391 const void* GetNativePointer() const 392 { 393 return nativePointerOrBytecodeArray_; 394 } 395 GetLiteralInfoMethodLiteral396 uint64_t GetLiteralInfo() const 397 { 398 return literalInfo_; 399 } 400 GetExtraLiteralInfoMethodLiteral401 uint64_t GetExtraLiteralInfo() const 402 { 403 return extraLiteralInfo_; 404 } 405 406 std::optional<std::set<uint32_t>> GetConcurrentRequestedModules(const JSPandaFile *jsPandaFile) const; 407 408 private: 409 enum class Index : size_t { 410 CALL_FIELD_INDEX = 0, 411 NATIVE_POINTER_OR_BYTECODE_ARRAY_INDEX, 412 LITERAL_INFO_INDEX, 413 EXTRA_LITERAL_INFO_INDEX, 414 NUM_OF_MEMBERS 415 }; 416 static_assert(static_cast<size_t>(Index::NUM_OF_MEMBERS) == NumOfTypes); 417 418 static std::pair<std::string_view, bool> GetMethodNameView(const JSPandaFile* jsPandaFile, EntityId methodId, 419 bool cpuProfiler = false); 420 SetMethodIdMethodLiteral421 void SetMethodId(EntityId methodId) 422 { 423 literalInfo_ = MethodIdBits::Update(literalInfo_, methodId.GetOffset()); 424 } 425 SetSlotSizeMethodLiteral426 void SetSlotSize(uint32_t size) 427 { 428 if (size > MAX_SLOT_SIZE) { 429 size = MAX_SLOT_SIZE; 430 } else if (size + EXTEND_SLOT_SIZE > INVALID_IC_SLOT && size <= INVALID_IC_SLOT) { 431 // for compatibility: ensure there's always 0xff slot in this situation 432 size = INVALID_IC_SLOT + 1; 433 } 434 literalInfo_ = SlotSizeBits::Update(literalInfo_, size); 435 } 436 437 alignas(EAS) uint64_t callField_ {0ULL}; 438 // Native method decides this filed is NativePointer or BytecodeArray pointer. 439 alignas(EAS) const void *nativePointerOrBytecodeArray_ {nullptr}; 440 // hotnessCounter, methodId and slotSize are encoded in literalInfo_. 441 alignas(EAS) uint64_t literalInfo_ {0ULL}; 442 // BuiltinId, FunctionKind are encoded in extraLiteralInfo_. 443 alignas(EAS) uint64_t extraLiteralInfo_ {0ULL}; 444 }; 445 STATIC_ASSERT_EQ_ARCH(sizeof(MethodLiteral), MethodLiteral::SizeArch32, MethodLiteral::SizeArch64); 446 } // namespace panda::ecmascript 447 448 #endif // ECMASCRIPT_JSPANDAFILE_METHOD_LITERAL_H 449