InlineCache.h 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. #define TypeWithAuxSlotTag(_t) \
  7. (reinterpret_cast<Type*>(reinterpret_cast<size_t>(_t) | InlineCacheAuxSlotTypeTag))
  8. #define TypeWithoutAuxSlotTag(_t) \
  9. (reinterpret_cast<Js::Type*>(reinterpret_cast<size_t>(_t) & ~InlineCacheAuxSlotTypeTag))
  10. #define TypeHasAuxSlotTag(_t) \
  11. (!!(reinterpret_cast<size_t>(_t) & InlineCacheAuxSlotTypeTag))
  12. // forward decl
  13. class JITType;
  14. struct InlineCacheData;
  15. template <class TAllocator> class JITTypeHolderBase;
  16. typedef JITTypeHolderBase<void> JITTypeHolder;
  17. typedef JITTypeHolderBase<Recycler> RecyclerJITTypeHolder;
  18. namespace Js
  19. {
  20. enum CacheType : byte
  21. {
  22. CacheType_None,
  23. CacheType_Local,
  24. CacheType_Proto,
  25. CacheType_LocalWithoutProperty,
  26. CacheType_Getter,
  27. CacheType_Setter,
  28. CacheType_TypeProperty,
  29. };
  30. enum SlotType : byte
  31. {
  32. SlotType_None,
  33. SlotType_Inline,
  34. SlotType_Aux,
  35. };
  36. struct PropertyCacheOperationInfo
  37. {
  38. PropertyCacheOperationInfo()
  39. : cacheType(CacheType_None), slotType(SlotType_None), isPolymorphic(false)
  40. {
  41. }
  42. CacheType cacheType;
  43. SlotType slotType;
  44. bool isPolymorphic;
  45. };
  46. struct JitTimeInlineCache;
  47. struct InlineCache
  48. {
  49. static const int CacheLayoutSelectorBitCount = 1;
  50. static const int RequiredAuxSlotCapacityBitCount = 15;
  51. static const bool IsPolymorphic = false;
  52. InlineCache() {}
  53. union
  54. {
  55. // Invariants:
  56. // - Type* fields do not overlap.
  57. // - "next" field is non-null iff the cache is linked in a list of proto-caches
  58. // (see ScriptContext::RegisterProtoInlineCache and ScriptContext::InvalidateProtoCaches).
  59. struct s_local
  60. {
  61. Type* type;
  62. // PatchPutValue caches here the type the object has before a new property is added.
  63. // If this type is hit again we can immediately change the object's type to "type"
  64. // and store the value into the slot "slotIndex".
  65. Type* typeWithoutProperty;
  66. union
  67. {
  68. struct
  69. {
  70. uint16 isLocal : 1;
  71. uint16 requiredAuxSlotCapacity : 15; // Maximum auxiliary slot capacity (for a path type) must be < 2^16
  72. };
  73. struct
  74. {
  75. uint16 rawUInt16; // Required for access from JIT-ed code
  76. };
  77. };
  78. uint16 slotIndex;
  79. } local;
  80. struct s_proto
  81. {
  82. uint16 isProto : 1;
  83. uint16 isMissing : 1;
  84. uint16 unused : 14;
  85. uint16 slotIndex;
  86. // It's OK for the type in proto layout to overlap with typeWithoutProperty in the local layout, because
  87. // we only use typeWithoutProperty on field stores, which can never have a proto layout.
  88. Type* type;
  89. DynamicObject* prototypeObject;
  90. } proto;
  91. struct s_accessor
  92. {
  93. DynamicObject *object;
  94. union
  95. {
  96. struct {
  97. uint16 isAccessor : 1;
  98. uint16 flags : 2;
  99. uint16 isOnProto : 1;
  100. uint16 unused : 12;
  101. };
  102. uint16 rawUInt16;
  103. };
  104. uint16 slotIndex;
  105. Type * type;
  106. } accessor;
  107. CompileAssert(sizeof(s_local) == sizeof(s_proto));
  108. CompileAssert(sizeof(s_local) == sizeof(s_accessor));
  109. } u;
  110. InlineCache** invalidationListSlotPtr;
  111. bool IsEmpty() const
  112. {
  113. return u.local.type == nullptr;
  114. }
  115. bool IsLocal() const
  116. {
  117. return u.local.isLocal;
  118. }
  119. bool IsProto() const
  120. {
  121. return u.proto.isProto;
  122. }
  123. DynamicObject * GetPrototypeObject() const
  124. {
  125. Assert(IsProto());
  126. return u.proto.prototypeObject;
  127. }
  128. DynamicObject * GetAccessorObject() const
  129. {
  130. Assert(IsAccessor());
  131. return u.accessor.object;
  132. }
  133. bool IsAccessor() const
  134. {
  135. return u.accessor.isAccessor;
  136. }
  137. bool IsAccessorOnProto() const
  138. {
  139. return IsAccessor() && u.accessor.isOnProto;
  140. }
  141. bool IsGetterAccessor() const
  142. {
  143. return IsAccessor() && !!(u.accessor.flags & InlineCacheGetterFlag);
  144. }
  145. bool IsGetterAccessorOnProto() const
  146. {
  147. return IsGetterAccessor() && u.accessor.isOnProto;
  148. }
  149. bool IsSetterAccessor() const
  150. {
  151. return IsAccessor() && !!(u.accessor.flags & InlineCacheSetterFlag);
  152. }
  153. bool IsSetterAccessorOnProto() const
  154. {
  155. return IsSetterAccessor() && u.accessor.isOnProto;
  156. }
  157. Type* GetRawType() const
  158. {
  159. return IsLocal() ? u.local.type : (IsProto() ? u.proto.type : (IsAccessor() ? u.accessor.type : nullptr));
  160. }
  161. Type* GetType() const
  162. {
  163. return TypeWithoutAuxSlotTag(GetRawType());
  164. }
  165. template<bool isAccessor>
  166. bool HasDifferentType(const bool isProto, const Type * type, const Type * typeWithoutProperty) const;
  167. bool HasType_Flags(const Type * type) const
  168. {
  169. return u.accessor.type == type || u.accessor.type == TypeWithAuxSlotTag(type);
  170. }
  171. bool HasDifferentType(const Type * type) const
  172. {
  173. return !IsEmpty() && GetType() != type;
  174. }
  175. bool RemoveFromInvalidationList()
  176. {
  177. if (this->invalidationListSlotPtr == nullptr)
  178. {
  179. return false;
  180. }
  181. Assert(*this->invalidationListSlotPtr == this);
  182. *this->invalidationListSlotPtr = nullptr;
  183. this->invalidationListSlotPtr = nullptr;
  184. return true;
  185. }
  186. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  187. const char16 *LayoutString() const
  188. {
  189. if (IsEmpty())
  190. {
  191. return _u("Empty");
  192. }
  193. if (IsLocal())
  194. {
  195. return _u("Local");
  196. }
  197. if (IsAccessor())
  198. {
  199. return _u("Accessor");
  200. }
  201. return _u("Proto");
  202. }
  203. #endif
  204. public:
  205. void CacheLocal(
  206. Type *const type,
  207. const PropertyId propertyId,
  208. const PropertyIndex propertyIndex,
  209. const bool isInlineSlot,
  210. Type *const typeWithoutProperty,
  211. int requiredAuxSlotCapacity,
  212. ScriptContext *const requestContext);
  213. void CacheProto(
  214. DynamicObject *const prototypeObjectWithProperty,
  215. const PropertyId propertyId,
  216. const PropertyIndex propertyIndex,
  217. const bool isInlineSlot,
  218. const bool isMissing,
  219. Type *const type,
  220. ScriptContext *const requestContext);
  221. void CacheAccessor(
  222. const bool isGetter,
  223. const PropertyId propertyId,
  224. const PropertyIndex propertyIndex,
  225. const bool isInlineSlot,
  226. Type *const type,
  227. DynamicObject *const object,
  228. const bool isOnProto,
  229. ScriptContext *const requestContext);
  230. template<
  231. bool CheckLocal,
  232. bool CheckProto,
  233. bool CheckAccessor,
  234. bool CheckMissing,
  235. bool ReturnOperationInfo,
  236. bool OutputExistence /*When set, propertyValue is true or false, representing whether the property exists on the instance not its actual value*/>
  237. bool TryGetProperty(
  238. Var const instance,
  239. RecyclableObject *const propertyObject,
  240. const PropertyId propertyId,
  241. Var *const propertyValue,
  242. ScriptContext *const requestContext,
  243. PropertyCacheOperationInfo *const operationInfo);
  244. template<
  245. bool CheckLocal,
  246. bool CheckLocalTypeWithoutProperty,
  247. bool CheckAccessor,
  248. bool ReturnOperationInfo>
  249. bool TrySetProperty(
  250. RecyclableObject *const object,
  251. const PropertyId propertyId,
  252. Var propertyValue,
  253. ScriptContext *const requestContext,
  254. PropertyCacheOperationInfo *const operationInfo,
  255. const PropertyOperationFlags propertyOperationFlags = PropertyOperation_None);
  256. bool PretendTryGetProperty(Type *const type, PropertyCacheOperationInfo * operationInfo) const;
  257. bool PretendTrySetProperty(Type *const type, Type *const oldType, PropertyCacheOperationInfo * operationInfo) const;
  258. void Clear();
  259. void RemoveFromInvalidationListAndClear(ThreadContext* threadContext);
  260. template <class TAllocator>
  261. InlineCache *Clone(TAllocator *const allocator);
  262. InlineCache *Clone(Js::PropertyId propertyId, ScriptContext* scriptContext);
  263. void CopyTo(PropertyId propertyId, ScriptContext * scriptContext, InlineCache * const clone);
  264. #if ENABLE_FIXED_FIELDS
  265. bool TryGetFixedMethodFromCache(Js::FunctionBody* functionBody, uint cacheId, Js::JavascriptFunction** pFixedMethod);
  266. #endif
  267. bool GetGetterSetter(RecyclableObject *object, RecyclableObject **callee);
  268. bool GetCallApplyTarget(RecyclableObject* obj, RecyclableObject **callee);
  269. static uint GetGetterFlagMask()
  270. {
  271. // First bit is marked for isAccessor in the accessor cache layout.
  272. return InlineCacheGetterFlag << 1;
  273. }
  274. static uint GetSetterFlagMask()
  275. {
  276. // First bit is marked for isAccessor in the accessor cache layout.
  277. return InlineCacheSetterFlag << 1;
  278. }
  279. static uint GetGetterSetterFlagMask()
  280. {
  281. // First bit is marked for isAccessor in the accessor cache layout.
  282. return (InlineCacheGetterFlag | InlineCacheSetterFlag) << 1;
  283. }
  284. static uint GetIsOnProtoFlagMask()
  285. {
  286. return InlineCacheIsOnProtoFlag << 1;
  287. }
  288. bool NeedsToBeRegisteredForProtoInvalidation() const;
  289. bool NeedsToBeRegisteredForStoreFieldInvalidation() const;
  290. #if DEBUG
  291. bool ConfirmCacheMiss(const Type * oldType, const PropertyValueInfo* info) const;
  292. bool NeedsToBeRegisteredForInvalidation() const;
  293. static void VerifyRegistrationForInvalidation(const InlineCache* cache, ScriptContext* scriptContext, Js::PropertyId propertyId);
  294. #endif
  295. #if DBG_DUMP
  296. void Dump();
  297. #endif
  298. private:
  299. // Write operation info if relevant
  300. template<bool ReturnOperationInfo> inline static void OutputOperationInfo(PropertyCacheOperationInfo *const operationInfo, CacheType cacheType, SlotType slotType);
  301. // Get the source object that supplies property values, based on the cache type and the starting object
  302. template<CacheType cacheType> inline DynamicObject* GetSourceObject(RecyclableObject *const propertyObject);
  303. // Get the source object that we should test for script context matching, based on the cache type and the starting object
  304. template<CacheType cacheType> inline RecyclableObject* GetSourceObjectForScriptContext(RecyclableObject *const propertyObject);
  305. // Get the slot index for the property, based on the cache type
  306. template<CacheType cacheType> inline int GetSlotIndex();
  307. // Get the property value, based on the source object and slot index
  308. template<SlotType slotType> inline static Var GetPropertyValue(DynamicObject* sourceObject, int slotIndex);
  309. // Set the output propertyValue and operationInfo
  310. template<
  311. bool OutputExistence,
  312. bool IsMissing,
  313. bool ReturnOperationInfo,
  314. CacheType cacheType,
  315. SlotType slotType>
  316. void OutputPropertyValueAndOperationInfo(
  317. Var const instance,
  318. RecyclableObject *const propertyObject,
  319. const PropertyId propertyId,
  320. Var *const propertyValue,
  321. ScriptContext *const requestContext,
  322. PropertyCacheOperationInfo *const operationInfo)
  323. {
  324. Assert(this->GetSourceObjectForScriptContext<cacheType>(propertyObject)->GetScriptContext() == requestContext); // we never cache a type from another script context
  325. OutputPropertyValue<OutputExistence, IsMissing, cacheType, slotType>::impl(this, instance, propertyObject, propertyId, propertyValue, requestContext);
  326. OutputOperationInfo<ReturnOperationInfo>(operationInfo, cacheType, slotType);
  327. }
  328. // Because C++ can't partially specialize functions, here's a struct. Calling the impl method on this struct
  329. // will set the output propertyValue.
  330. template<
  331. bool OutputExistence,
  332. bool IsMissing,
  333. CacheType cacheType,
  334. SlotType slotType>
  335. struct OutputPropertyValue {};
  336. template<bool IsMissing, CacheType cacheType, SlotType slotType>
  337. struct OutputPropertyValue<true /*OutputExistence*/, IsMissing, cacheType, slotType>
  338. {
  339. static void impl(
  340. InlineCache* cache,
  341. Var const instance,
  342. RecyclableObject *const propertyObject,
  343. const PropertyId propertyId,
  344. Var *const propertyValue,
  345. ScriptContext *const requestContext)
  346. {
  347. *propertyValue = IsMissing ? requestContext->GetLibrary()->GetFalse() : requestContext->GetLibrary()->GetTrue();
  348. Assert(!JavascriptOperators::HasProperty(propertyObject, propertyId) == IsMissing);
  349. }
  350. };
  351. template<bool IsMissing, SlotType slotType>
  352. struct OutputPropertyValue<false /*OutputExistence*/, IsMissing, CacheType::CacheType_Getter, slotType>
  353. {
  354. static void impl(
  355. InlineCache* cache,
  356. Var const instance,
  357. RecyclableObject *const propertyObject,
  358. const PropertyId propertyId,
  359. Var *const propertyValue,
  360. ScriptContext *const requestContext)
  361. {
  362. Assert(cache->u.accessor.flags & InlineCacheGetterFlag);
  363. RecyclableObject * function;
  364. if (cache->u.accessor.isOnProto)
  365. {
  366. function = RecyclableObject::UnsafeFromVar(cache->GetPropertyValue<slotType>(cache->u.accessor.object, cache->u.accessor.slotIndex));
  367. }
  368. else
  369. {
  370. function = RecyclableObject::UnsafeFromVar(cache->GetPropertyValue<slotType>(DynamicObject::UnsafeFromVar(propertyObject), cache->u.accessor.slotIndex));
  371. }
  372. *propertyValue = JavascriptOperators::CallGetter(function, instance, requestContext);
  373. // Can't assert because the getter could have a side effect
  374. #ifdef CHKGETTER
  375. Assert(JavascriptOperators::Equal(*propertyValue, JavascriptOperators::GetProperty(propertyObject, propertyId, requestContext), requestContext));
  376. #endif
  377. }
  378. };
  379. template<bool IsMissing, CacheType cacheType, SlotType slotType>
  380. struct OutputPropertyValue<false /*OutputExistence*/, IsMissing, cacheType, slotType>
  381. {
  382. static void impl(
  383. InlineCache* cache,
  384. Var const instance,
  385. RecyclableObject *const propertyObject,
  386. const PropertyId propertyId,
  387. Var *const propertyValue,
  388. ScriptContext *const requestContext)
  389. {
  390. *propertyValue = InlineCache::GetPropertyValue<slotType>(cache->GetSourceObject<cacheType>(propertyObject), cache->GetSlotIndex<cacheType>());
  391. #if DBG
  392. Var slowPathValue = JavascriptOperators::GetProperty(propertyObject, propertyId, requestContext);
  393. Var rootObjectValue = nullptr;
  394. if (RootObjectBase::Is(propertyObject))
  395. {
  396. rootObjectValue = JavascriptOperators::GetRootProperty(propertyObject, propertyId, requestContext);
  397. }
  398. Assert(*propertyValue == slowPathValue ||
  399. (RootObjectBase::Is(propertyObject) && *propertyValue == rootObjectValue) ||
  400. // In some cases, such as CustomExternalObject, if implicit calls are disabled GetPropertyQuery may return null. See CustomExternalObject::GetPropertyQuery for an example.
  401. (slowPathValue == requestContext->GetLibrary()->GetNull() && requestContext->GetThreadContext()->IsDisableImplicitCall() && propertyObject->GetType()->IsExternal()));
  402. #endif
  403. }
  404. };
  405. };
  406. #if defined(TARGET_32)
  407. CompileAssert(sizeof(InlineCache) == 0x10);
  408. #else
  409. CompileAssert(sizeof(InlineCache) == 0x20);
  410. #endif
  411. CompileAssert(sizeof(InlineCache) == sizeof(InlineCacheAllocator::CacheLayout));
  412. CompileAssert(offsetof(InlineCache, invalidationListSlotPtr) == offsetof(InlineCacheAllocator::CacheLayout, strongRef));
  413. class PolymorphicInlineCache : public FinalizableObject
  414. {
  415. DECLARE_RECYCLER_VERIFY_MARK_FRIEND()
  416. #ifdef INLINE_CACHE_STATS
  417. friend class Js::ScriptContext;
  418. #endif
  419. public:
  420. static const bool IsPolymorphic = true;
  421. protected:
  422. FieldNoBarrier(InlineCache *) inlineCaches;
  423. Field(uint16) size;
  424. Field(bool) ignoreForEquivalentObjTypeSpec;
  425. Field(bool) cloneForJitTimeUse;
  426. Field(int32) inlineCachesFillInfo;
  427. PolymorphicInlineCache(InlineCache * inlineCaches, uint16 size)
  428. : inlineCaches(inlineCaches), size(size), ignoreForEquivalentObjTypeSpec(false), cloneForJitTimeUse(true), inlineCachesFillInfo(0)
  429. {
  430. }
  431. public:
  432. bool CanAllocateBigger() { return GetSize() < MaxPolymorphicInlineCacheSize; }
  433. static uint16 GetNextSize(uint16 currentSize)
  434. {
  435. if (currentSize == MaxPolymorphicInlineCacheSize)
  436. {
  437. return 0;
  438. }
  439. else if (currentSize == MinPropertyStringInlineCacheSize)
  440. {
  441. CompileAssert(MinPropertyStringInlineCacheSize < MinPolymorphicInlineCacheSize);
  442. return MinPolymorphicInlineCacheSize;
  443. }
  444. else
  445. {
  446. Assert(currentSize >= MinPolymorphicInlineCacheSize && currentSize <= (MaxPolymorphicInlineCacheSize / 2));
  447. return currentSize * 2;
  448. }
  449. }
  450. template<bool isAccessor>
  451. bool HasDifferentType(const bool isProto, const Type * type, const Type * typeWithoutProperty) const;
  452. bool HasType_Flags(const Type * type) const;
  453. InlineCache * GetInlineCaches() const { return inlineCaches; }
  454. uint16 GetSize() const { return size; }
  455. bool GetIgnoreForEquivalentObjTypeSpec() const { return this->ignoreForEquivalentObjTypeSpec; }
  456. void SetIgnoreForEquivalentObjTypeSpec(bool value) { this->ignoreForEquivalentObjTypeSpec = value; }
  457. bool GetCloneForJitTimeUse() const { return this->cloneForJitTimeUse; }
  458. void SetCloneForJitTimeUse(bool value) { this->cloneForJitTimeUse = value; }
  459. uint32 GetInlineCachesFillInfo() { return this->inlineCachesFillInfo; }
  460. void UpdateInlineCachesFillInfo(uint32 index, bool set);
  461. bool IsFull();
  462. void Clear(Type * type);
  463. virtual void Dispose(bool isShutdown) override { };
  464. virtual void Mark(Recycler *recycler) override { AssertMsg(false, "Mark called on object that isn't TrackableObject"); }
  465. void CacheLocal(
  466. Type *const type,
  467. const PropertyId propertyId,
  468. const PropertyIndex propertyIndex,
  469. const bool isInlineSlot,
  470. Type *const typeWithoutProperty,
  471. int requiredAuxSlotCapacity,
  472. ScriptContext *const requestContext);
  473. void CacheProto(
  474. DynamicObject *const prototypeObjectWithProperty,
  475. const PropertyId propertyId,
  476. const PropertyIndex propertyIndex,
  477. const bool isInlineSlot,
  478. const bool isMissing,
  479. Type *const type,
  480. ScriptContext *const requestContext);
  481. void CacheAccessor(
  482. const bool isGetter,
  483. const PropertyId propertyId,
  484. const PropertyIndex propertyIndex,
  485. const bool isInlineSlot,
  486. Type *const type,
  487. DynamicObject *const object,
  488. const bool isOnProto,
  489. ScriptContext *const requestContext);
  490. template<
  491. bool CheckLocal,
  492. bool CheckProto,
  493. bool CheckAccessor,
  494. bool CheckMissing,
  495. bool IsInlineCacheAvailable,
  496. bool ReturnOperationInfo,
  497. bool OutputExistence /*When set, propertyValue is true or false, representing whether the property exists on the instance not its actual value*/>
  498. bool TryGetProperty(
  499. Var const instance,
  500. RecyclableObject *const propertyObject,
  501. const PropertyId propertyId,
  502. Var *const propertyValue,
  503. ScriptContext *const requestContext,
  504. PropertyCacheOperationInfo *const operationInfo,
  505. InlineCache *const inlineCacheToPopulate);
  506. template<
  507. bool CheckLocal,
  508. bool CheckLocalTypeWithoutProperty,
  509. bool CheckAccessor,
  510. bool ReturnOperationInfo,
  511. bool PopulateInlineCache>
  512. bool TrySetProperty(
  513. RecyclableObject *const object,
  514. const PropertyId propertyId,
  515. Var propertyValue,
  516. ScriptContext *const requestContext,
  517. PropertyCacheOperationInfo *const operationInfo,
  518. InlineCache *const inlineCacheToPopulate,
  519. const PropertyOperationFlags propertyOperationFlags = PropertyOperation_None);
  520. bool PretendTryGetProperty(Type *const type, PropertyCacheOperationInfo * operationInfo);
  521. bool PretendTrySetProperty(Type *const type, Type *const oldType, PropertyCacheOperationInfo * operationInfo);
  522. void CopyTo(PropertyId propertyId, ScriptContext* scriptContext, PolymorphicInlineCache *const clone);
  523. #if DBG_DUMP
  524. void Dump();
  525. #endif
  526. uint GetInlineCacheIndexForType(const Type * type) const
  527. {
  528. return (((size_t)type) >> PolymorphicInlineCacheShift) & (GetSize() - 1);
  529. }
  530. #ifdef INLINE_CACHE_STATS
  531. virtual void PrintStats(InlineCacheData *data) const = 0;
  532. #endif
  533. virtual ScriptContext* GetScriptContext() const = 0;
  534. static uint32 GetOffsetOfSize() { return offsetof(Js::PolymorphicInlineCache, size); }
  535. static uint32 GetOffsetOfInlineCaches() { return offsetof(Js::PolymorphicInlineCache, inlineCaches); }
  536. private:
  537. uint GetNextInlineCacheIndex(uint index) const
  538. {
  539. if (++index == GetSize())
  540. {
  541. index = 0;
  542. }
  543. return index;
  544. }
  545. template<bool CheckLocal, bool CheckProto, bool CheckAccessor>
  546. void CloneInlineCacheToEmptySlotInCollision(Type *const type, uint index);
  547. #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
  548. template <typename TDelegate>
  549. bool CheckClonedInlineCache(uint inlineCacheIndex, TDelegate mapper);
  550. #endif
  551. #if INTRUSIVE_TESTTRACE_PolymorphicInlineCache
  552. uint GetEntryCount()
  553. {
  554. uint count = 0;
  555. for (uint i = 0; i < size; ++i)
  556. {
  557. if (!inlineCaches[i].IsEmpty())
  558. {
  559. count++;
  560. }
  561. }
  562. return count;
  563. }
  564. #endif
  565. };
  566. class FunctionBodyPolymorphicInlineCache sealed : public PolymorphicInlineCache
  567. {
  568. private:
  569. FunctionBody * functionBody;
  570. // DList chaining all polymorphic inline caches of a FunctionBody together.
  571. // Since PolymorphicInlineCache is a leaf object, these references do not keep
  572. // the polymorphic inline caches alive. When a PolymorphicInlineCache is finalized,
  573. // it removes itself from the list and deletes its inline cache array.
  574. // We maintain this linked list of polymorphic caches because when we allocate a larger cache,
  575. // the old one might still be used by some code on the stack. Consequently, we can't release
  576. // the inline cache array back to the arena allocator.
  577. FunctionBodyPolymorphicInlineCache * next;
  578. FunctionBodyPolymorphicInlineCache * prev;
  579. FunctionBodyPolymorphicInlineCache(InlineCache * inlineCaches, uint16 size, FunctionBody * functionBody)
  580. : PolymorphicInlineCache(inlineCaches, size), functionBody(functionBody), next(nullptr), prev(nullptr)
  581. {
  582. Assert((size == 0 && inlineCaches == nullptr) ||
  583. (inlineCaches != nullptr && size >= MinPolymorphicInlineCacheSize && size <= MaxPolymorphicInlineCacheSize));
  584. }
  585. public:
  586. static FunctionBodyPolymorphicInlineCache * New(uint16 size, FunctionBody * functionBody);
  587. #ifdef INLINE_CACHE_STATS
  588. virtual void PrintStats(InlineCacheData *data) const override;
  589. #endif
  590. virtual ScriptContext* GetScriptContext() const override;
  591. virtual void Finalize(bool isShutdown) override;
  592. };
  593. class ScriptContextPolymorphicInlineCache sealed : public PolymorphicInlineCache
  594. {
  595. private:
  596. Field(JavascriptLibrary*) javascriptLibrary;
  597. ScriptContextPolymorphicInlineCache(InlineCache * inlineCaches, uint16 size, JavascriptLibrary * javascriptLibrary)
  598. : PolymorphicInlineCache(inlineCaches, size), javascriptLibrary(javascriptLibrary)
  599. {
  600. Assert((size == 0 && inlineCaches == nullptr) ||
  601. (inlineCaches != nullptr && size >= MinPropertyStringInlineCacheSize && size <= MaxPolymorphicInlineCacheSize));
  602. }
  603. public:
  604. static ScriptContextPolymorphicInlineCache * New(uint16 size, JavascriptLibrary * javascriptLibrary);
  605. #ifdef INLINE_CACHE_STATS
  606. virtual void PrintStats(InlineCacheData *data) const override;
  607. #endif
  608. virtual ScriptContext* GetScriptContext() const override;
  609. virtual void Finalize(bool isShutdown) override;
  610. };
  611. // Caches the result of an instanceof operator over a type and a function
  612. struct IsInstInlineCache
  613. {
  614. Type * type; // The type of object operand an inline cache caches a result for
  615. JavascriptFunction * function; // The function operand an inline cache caches a result for
  616. JavascriptBoolean * result; // The result of doing (object instanceof function) where object->type == this->type
  617. IsInstInlineCache * next; // Used to link together caches that have the same function operand
  618. public:
  619. bool IsEmpty() const { return type == nullptr; }
  620. bool TryGetResult(Var instance, JavascriptFunction * function, JavascriptBoolean ** result);
  621. void Cache(Type * instanceType, JavascriptFunction * function, JavascriptBoolean * result, ScriptContext * scriptContext);
  622. void Unregister(ScriptContext * scriptContext);
  623. void Clear();
  624. static uint32 OffsetOfFunction();
  625. static uint32 OffsetOfResult();
  626. static uint32 OffsetOfType();
  627. private:
  628. void Set(Type * instanceType, JavascriptFunction * function, JavascriptBoolean * result);
  629. };
  630. // Two-entry Type-indexed circular cache
  631. // cache IsConcatSpreadable() result unless user-defined [@@isConcatSpreadable] exists
  632. class IsConcatSpreadableCache
  633. {
  634. Type *type0, *type1;
  635. int lastAccess;
  636. BOOL result0, result1;
  637. public:
  638. IsConcatSpreadableCache() :
  639. type0(nullptr),
  640. type1(nullptr),
  641. result0(FALSE),
  642. result1(FALSE),
  643. lastAccess(1)
  644. {
  645. }
  646. bool TryGetIsConcatSpreadable(Type *type, _Out_ BOOL *result)
  647. {
  648. Assert(type != nullptr);
  649. Assert(result != nullptr);
  650. *result = FALSE;
  651. if (type0 == type)
  652. {
  653. *result = result0;
  654. lastAccess = 0;
  655. return true;
  656. }
  657. if (type1 == type)
  658. {
  659. *result = result1;
  660. lastAccess = 1;
  661. return true;
  662. }
  663. return false;
  664. }
  665. void CacheIsConcatSpreadable(Type *type, BOOL result)
  666. {
  667. Assert(type != nullptr);
  668. if (lastAccess == 0)
  669. {
  670. type1 = type;
  671. result1 = result;
  672. lastAccess = 1;
  673. }
  674. else
  675. {
  676. type0 = type;
  677. result0 = result;
  678. lastAccess = 0;
  679. }
  680. }
  681. void Invalidate()
  682. {
  683. type0 = nullptr;
  684. type1 = nullptr;
  685. }
  686. };
  687. #if defined(TARGET_32)
  688. CompileAssert(sizeof(IsInstInlineCache) == 0x10);
  689. #else
  690. CompileAssert(sizeof(IsInstInlineCache) == 0x20);
  691. #endif
  692. }