InlineCache.inl 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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. namespace Js
  7. {
  8. template<
  9. bool CheckLocal,
  10. bool CheckProto,
  11. bool CheckAccessor,
  12. bool CheckMissing,
  13. bool ReturnOperationInfo,
  14. bool OutputExistence /*When set, propertyValue represents whether the property exists on the instance, not its actual value*/>
  15. bool InlineCache::TryGetProperty(
  16. Var const instance,
  17. RecyclableObject *const propertyObject,
  18. const PropertyId propertyId,
  19. Var *const propertyValue,
  20. ScriptContext *const requestContext,
  21. PropertyCacheOperationInfo *const operationInfo)
  22. {
  23. CompileAssert(CheckLocal || CheckProto || CheckAccessor || CheckMissing);
  24. Assert(!ReturnOperationInfo || operationInfo);
  25. CompileAssert(!ReturnOperationInfo || (CheckLocal && CheckProto && CheckAccessor));
  26. Assert(instance);
  27. Assert(propertyObject);
  28. Assert(propertyId != Constants::NoProperty);
  29. Assert(propertyValue);
  30. Assert(requestContext);
  31. DebugOnly(VerifyRegistrationForInvalidation(this, requestContext, propertyId));
  32. Type *const type = propertyObject->GetType();
  33. if (CheckLocal && type == u.local.type)
  34. {
  35. OutputPropertyValueAndOperationInfo<OutputExistence, false /*IsMissing*/, ReturnOperationInfo, CacheType_Local, SlotType_Inline>(
  36. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  37. return true;
  38. }
  39. if (CheckLocal && TypeWithAuxSlotTag(type) == u.local.type)
  40. {
  41. OutputPropertyValueAndOperationInfo<OutputExistence, false /*IsMissing*/, ReturnOperationInfo, CacheType_Local, SlotType_Aux>(
  42. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  43. return true;
  44. }
  45. if (CheckProto && type == u.proto.type && !this->u.proto.isMissing)
  46. {
  47. OutputPropertyValueAndOperationInfo<OutputExistence, false /*IsMissing*/, ReturnOperationInfo, CacheType_Proto, SlotType_Inline>(
  48. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  49. return true;
  50. }
  51. if (CheckProto && TypeWithAuxSlotTag(type) == u.proto.type && !this->u.proto.isMissing)
  52. {
  53. OutputPropertyValueAndOperationInfo<OutputExistence, false /*IsMissing*/, ReturnOperationInfo, CacheType_Proto, SlotType_Aux>(
  54. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  55. return true;
  56. }
  57. if (CheckAccessor && type == u.accessor.type)
  58. {
  59. OutputPropertyValueAndOperationInfo<OutputExistence, false /*IsMissing*/, ReturnOperationInfo, CacheType_Getter, SlotType_Inline>(
  60. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  61. return true;
  62. }
  63. if (CheckAccessor && TypeWithAuxSlotTag(type) == u.accessor.type)
  64. {
  65. OutputPropertyValueAndOperationInfo<OutputExistence, false /*IsMissing*/, ReturnOperationInfo, CacheType_Getter, SlotType_Aux>(
  66. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  67. return true;
  68. }
  69. if (CheckMissing && type == u.proto.type && this->u.proto.isMissing)
  70. {
  71. OutputPropertyValueAndOperationInfo<OutputExistence, true /*IsMissing*/, ReturnOperationInfo, CacheType_Proto, SlotType_Inline>(
  72. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  73. #ifdef MISSING_PROPERTY_STATS
  74. if (PHASE_STATS1(MissingPropertyCachePhase))
  75. {
  76. requestContext->RecordMissingPropertyHit();
  77. }
  78. #endif
  79. return true;
  80. }
  81. if (CheckMissing && TypeWithAuxSlotTag(type) == u.proto.type && this->u.proto.isMissing)
  82. {
  83. OutputPropertyValueAndOperationInfo<OutputExistence, true /*IsMissing*/, ReturnOperationInfo, CacheType_Proto, SlotType_Aux>(
  84. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  85. #ifdef MISSING_PROPERTY_STATS
  86. if (PHASE_STATS1(MissingPropertyCachePhase))
  87. {
  88. requestContext->RecordMissingPropertyHit();
  89. }
  90. #endif
  91. return true;
  92. }
  93. return false;
  94. }
  95. template<> inline void InlineCache::OutputOperationInfo<true>(PropertyCacheOperationInfo *const operationInfo, CacheType cacheType, SlotType slotType)
  96. {
  97. operationInfo->cacheType = cacheType;
  98. operationInfo->slotType = slotType;
  99. }
  100. template<> inline void InlineCache::OutputOperationInfo<false>(PropertyCacheOperationInfo *const operationInfo, CacheType cacheType, SlotType slotType)
  101. {
  102. }
  103. template<> inline DynamicObject* InlineCache::GetSourceObject<CacheType::CacheType_Local>(RecyclableObject *const propertyObject)
  104. {
  105. return DynamicObject::UnsafeFromVar(propertyObject);
  106. }
  107. template<> inline DynamicObject* InlineCache::GetSourceObject<CacheType::CacheType_Proto>(RecyclableObject *const propertyObject)
  108. {
  109. return u.proto.prototypeObject;
  110. }
  111. template<> inline RecyclableObject* InlineCache::GetSourceObjectForScriptContext<CacheType::CacheType_Local>(RecyclableObject *const propertyObject)
  112. {
  113. return propertyObject;
  114. }
  115. template<> inline RecyclableObject* InlineCache::GetSourceObjectForScriptContext<CacheType::CacheType_Proto>(RecyclableObject *const propertyObject)
  116. {
  117. return u.proto.prototypeObject;
  118. }
  119. template<> inline RecyclableObject* InlineCache::GetSourceObjectForScriptContext<CacheType::CacheType_Getter>(RecyclableObject *const propertyObject)
  120. {
  121. return propertyObject;
  122. }
  123. template<> inline int InlineCache::GetSlotIndex<CacheType::CacheType_Local>()
  124. {
  125. return u.local.slotIndex;
  126. }
  127. template<> inline int InlineCache::GetSlotIndex<CacheType::CacheType_Proto>()
  128. {
  129. return u.proto.slotIndex;
  130. }
  131. template<> inline Var InlineCache::GetPropertyValue<SlotType::SlotType_Inline>(DynamicObject* sourceObject, int slotIndex)
  132. {
  133. return sourceObject->GetInlineSlot(slotIndex);
  134. }
  135. template<> inline Var InlineCache::GetPropertyValue<SlotType::SlotType_Aux>(DynamicObject* sourceObject, int slotIndex)
  136. {
  137. return sourceObject->GetAuxSlot(slotIndex);
  138. }
  139. template<
  140. bool CheckLocal,
  141. bool CheckLocalTypeWithoutProperty,
  142. bool CheckAccessor,
  143. bool ReturnOperationInfo>
  144. bool InlineCache::TrySetProperty(
  145. RecyclableObject *const object,
  146. const PropertyId propertyId,
  147. Var propertyValue,
  148. ScriptContext *const requestContext,
  149. PropertyCacheOperationInfo *const operationInfo,
  150. const PropertyOperationFlags propertyOperationFlags)
  151. {
  152. CompileAssert(CheckLocal || CheckLocalTypeWithoutProperty || CheckAccessor);
  153. Assert(!ReturnOperationInfo || operationInfo);
  154. CompileAssert(!ReturnOperationInfo || (CheckLocal && CheckLocalTypeWithoutProperty && CheckAccessor));
  155. Assert(object);
  156. Assert(propertyId != Constants::NoProperty);
  157. Assert(requestContext);
  158. DebugOnly(VerifyRegistrationForInvalidation(this, requestContext, propertyId));
  159. #if DBG
  160. const bool isRoot = (propertyOperationFlags & PropertyOperation_Root) != 0;
  161. bool canSetField; // To verify if we can set a field on the object
  162. Var setterValue = nullptr;
  163. {
  164. // We need to disable implicit call to ensure the check doesn't cause unwanted side effects in debug code
  165. // Save old disableImplicitFlags and implicitCallFlags and disable implicit call and exception
  166. ThreadContext * threadContext = requestContext->GetThreadContext();
  167. DisableImplicitFlags disableImplicitFlags = *threadContext->GetAddressOfDisableImplicitFlags();
  168. Js::ImplicitCallFlags implicitCallFlags = threadContext->GetImplicitCallFlags();
  169. threadContext->ClearImplicitCallFlags();
  170. *threadContext->GetAddressOfDisableImplicitFlags() = DisableImplicitCallAndExceptionFlag;
  171. DescriptorFlags flags = DescriptorFlags::None;
  172. canSetField = !JavascriptOperators::CheckPrototypesForAccessorOrNonWritablePropertySlow(object, propertyId, &setterValue, &flags, isRoot, requestContext);
  173. if (threadContext->GetImplicitCallFlags() != Js::ImplicitCall_None)
  174. {
  175. canSetField = true; // If there was an implicit call, inconclusive. Disable debug check.
  176. setterValue = nullptr;
  177. }
  178. else
  179. if ((flags & Accessor) == Accessor)
  180. {
  181. Assert(setterValue != nullptr);
  182. }
  183. // Restore old disableImplicitFlags and implicitCallFlags
  184. *threadContext->GetAddressOfDisableImplicitFlags() = disableImplicitFlags;
  185. threadContext->SetImplicitCallFlags(implicitCallFlags);
  186. }
  187. #endif
  188. Type *const type = object->GetType();
  189. if (CheckLocal && type == u.local.type)
  190. {
  191. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  192. Assert(isRoot || object->GetPropertyIndex(propertyId) == DynamicObject::FromVar(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(u.local.slotIndex, true));
  193. Assert(!isRoot || RootObjectBase::FromVar(object)->GetRootPropertyIndex(propertyId) == DynamicObject::FromVar(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(u.local.slotIndex, true));
  194. Assert(object->CanStorePropertyValueDirectly(propertyId, isRoot));
  195. DynamicObject::UnsafeFromVar(object)->SetInlineSlot(SetSlotArgumentsRoot(propertyId, isRoot, u.local.slotIndex, propertyValue));
  196. if (ReturnOperationInfo)
  197. {
  198. operationInfo->cacheType = CacheType_Local;
  199. operationInfo->slotType = SlotType_Inline;
  200. }
  201. Assert(canSetField);
  202. return true;
  203. }
  204. if (CheckLocal && TypeWithAuxSlotTag(type) == u.local.type)
  205. {
  206. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  207. Assert(isRoot || object->GetPropertyIndex(propertyId) == DynamicObject::FromVar(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(u.local.slotIndex, false));
  208. Assert(!isRoot || RootObjectBase::FromVar(object)->GetRootPropertyIndex(propertyId) == DynamicObject::FromVar(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(u.local.slotIndex, false));
  209. Assert(object->CanStorePropertyValueDirectly(propertyId, isRoot));
  210. DynamicObject::UnsafeFromVar(object)->SetAuxSlot(SetSlotArgumentsRoot(propertyId, isRoot, u.local.slotIndex, propertyValue));
  211. if (ReturnOperationInfo)
  212. {
  213. operationInfo->cacheType = CacheType_Local;
  214. operationInfo->slotType = SlotType_Aux;
  215. }
  216. Assert(canSetField);
  217. return true;
  218. }
  219. if (CheckLocalTypeWithoutProperty && type == u.local.typeWithoutProperty)
  220. {
  221. // CAREFUL! CheckIfPrototypeChainHasOnlyWritableDataProperties may do allocation that triggers GC and
  222. // clears this cache, so save any info that is needed from the cache before calling those functions.
  223. Type *const typeWithProperty = u.local.type;
  224. const PropertyIndex propertyIndex = u.local.slotIndex;
  225. #if DBG
  226. uint16 newAuxSlotCapacity = u.local.requiredAuxSlotCapacity;
  227. #endif
  228. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  229. Assert(typeWithProperty);
  230. Assert(DynamicType::Is(typeWithProperty));
  231. Assert(((DynamicType*)typeWithProperty)->GetIsShared());
  232. Assert(((DynamicType*)typeWithProperty)->GetTypeHandler()->IsPathTypeHandler());
  233. AssertMsg(!((DynamicType*)u.local.typeWithoutProperty)->GetTypeHandler()->GetIsPrototype(), "Why did we cache a property add for a prototype?");
  234. Assert(((DynamicType*)typeWithProperty)->GetTypeHandler()->CanStorePropertyValueDirectly((const DynamicObject*)object, propertyId, isRoot));
  235. DynamicObject *const dynamicObject = DynamicObject::UnsafeFromVar(object);
  236. // If we're adding a property to an inlined slot, we should never need to adjust auxiliary slot array size.
  237. Assert(newAuxSlotCapacity == 0);
  238. dynamicObject->type = typeWithProperty;
  239. Assert(isRoot || object->GetPropertyIndex(propertyId) == DynamicObject::FromVar(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(propertyIndex, true));
  240. Assert(!isRoot || RootObjectBase::FromVar(object)->GetRootPropertyIndex(propertyId) == DynamicObject::FromVar(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(propertyIndex, true));
  241. dynamicObject->SetInlineSlot(SetSlotArgumentsRoot(propertyId, isRoot, propertyIndex, propertyValue));
  242. if (ReturnOperationInfo)
  243. {
  244. operationInfo->cacheType = CacheType_LocalWithoutProperty;
  245. operationInfo->slotType = SlotType_Inline;
  246. }
  247. return true;
  248. }
  249. if (CheckLocalTypeWithoutProperty && TypeWithAuxSlotTag(type) == u.local.typeWithoutProperty)
  250. {
  251. // CAREFUL! CheckIfPrototypeChainHasOnlyWritableDataProperties or AdjustSlots may do allocation that triggers GC and
  252. // clears this cache, so save any info that is needed from the cache before calling those functions.
  253. Type *const typeWithProperty = TypeWithoutAuxSlotTag(u.local.type);
  254. const PropertyIndex propertyIndex = u.local.slotIndex;
  255. uint16 newAuxSlotCapacity = u.local.requiredAuxSlotCapacity;
  256. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  257. Assert(typeWithProperty);
  258. Assert(DynamicType::Is(typeWithProperty));
  259. Assert(((DynamicType*)typeWithProperty)->GetIsShared());
  260. Assert(((DynamicType*)typeWithProperty)->GetTypeHandler()->IsPathTypeHandler());
  261. AssertMsg(!((DynamicType*)TypeWithoutAuxSlotTag(u.local.typeWithoutProperty))->GetTypeHandler()->GetIsPrototype(), "Why did we cache a property add for a prototype?");
  262. Assert(((DynamicType*)typeWithProperty)->GetTypeHandler()->CanStorePropertyValueDirectly((const DynamicObject*)object, propertyId, isRoot));
  263. DynamicObject *const dynamicObject = DynamicObject::UnsafeFromVar(object);
  264. if (newAuxSlotCapacity > 0)
  265. {
  266. DynamicTypeHandler::AdjustSlots(
  267. dynamicObject,
  268. static_cast<DynamicType *>(typeWithProperty)->GetTypeHandler()->GetInlineSlotCapacity(),
  269. newAuxSlotCapacity);
  270. }
  271. dynamicObject->type = typeWithProperty;
  272. Assert(isRoot || object->GetPropertyIndex(propertyId) == DynamicObject::FromVar(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(propertyIndex, false));
  273. Assert(!isRoot || RootObjectBase::FromVar(object)->GetRootPropertyIndex(propertyId) == DynamicObject::FromVar(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(propertyIndex, false));
  274. dynamicObject->SetAuxSlot(SetSlotArgumentsRoot(propertyId, isRoot, propertyIndex, propertyValue));
  275. if (ReturnOperationInfo)
  276. {
  277. operationInfo->cacheType = CacheType_LocalWithoutProperty;
  278. operationInfo->slotType = SlotType_Aux;
  279. }
  280. return true;
  281. }
  282. if (CheckAccessor && type == u.accessor.type)
  283. {
  284. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  285. Assert(u.accessor.flags & InlineCacheSetterFlag);
  286. RecyclableObject * function;
  287. if (u.accessor.isOnProto)
  288. {
  289. function = RecyclableObject::UnsafeFromVar(u.accessor.object->GetInlineSlot(u.accessor.slotIndex));
  290. }
  291. else
  292. {
  293. function = RecyclableObject::UnsafeFromVar(DynamicObject::FromVar(object)->GetInlineSlot(u.accessor.slotIndex));
  294. }
  295. Assert(setterValue == nullptr || setterValue == function);
  296. if (!JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, function, requestContext) &&
  297. !JavascriptError::ThrowIfNotExtensibleUndefinedSetter(propertyOperationFlags, function, requestContext))
  298. {
  299. Js::JavascriptOperators::CallSetter(function, object, propertyValue, requestContext);
  300. }
  301. if (ReturnOperationInfo)
  302. {
  303. operationInfo->cacheType = CacheType_Setter;
  304. operationInfo->slotType = SlotType_Inline;
  305. }
  306. return true;
  307. }
  308. if (CheckAccessor && TypeWithAuxSlotTag(type) == u.accessor.type)
  309. {
  310. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  311. Assert(u.accessor.flags & InlineCacheSetterFlag);
  312. RecyclableObject * function;
  313. if (u.accessor.isOnProto)
  314. {
  315. function = RecyclableObject::UnsafeFromVar(u.accessor.object->GetAuxSlot(u.accessor.slotIndex));
  316. }
  317. else
  318. {
  319. function = RecyclableObject::UnsafeFromVar(DynamicObject::FromVar(object)->GetAuxSlot(u.accessor.slotIndex));
  320. }
  321. Assert(setterValue == nullptr || setterValue == function);
  322. if (!JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, function, requestContext) &&
  323. !JavascriptError::ThrowIfNotExtensibleUndefinedSetter(propertyOperationFlags, function, requestContext))
  324. {
  325. Js::JavascriptOperators::CallSetter(function, object, propertyValue, requestContext);
  326. }
  327. if (ReturnOperationInfo)
  328. {
  329. operationInfo->cacheType = CacheType_Setter;
  330. operationInfo->slotType = SlotType_Aux;
  331. }
  332. return true;
  333. }
  334. return false;
  335. }
  336. template<
  337. bool CheckLocal,
  338. bool CheckProto,
  339. bool CheckAccessor>
  340. void PolymorphicInlineCache::CloneInlineCacheToEmptySlotInCollision(Type * const type, uint inlineCacheIndex)
  341. {
  342. if (CheckLocal && (inlineCaches[inlineCacheIndex].u.local.type == type || inlineCaches[inlineCacheIndex].u.local.type == TypeWithAuxSlotTag(type)))
  343. {
  344. return;
  345. }
  346. if (CheckProto && (inlineCaches[inlineCacheIndex].u.proto.type == type || inlineCaches[inlineCacheIndex].u.proto.type == TypeWithAuxSlotTag(type)))
  347. {
  348. return;
  349. }
  350. if (CheckAccessor && (inlineCaches[inlineCacheIndex].u.accessor.type == type || inlineCaches[inlineCacheIndex].u.accessor.type == TypeWithAuxSlotTag(type)))
  351. {
  352. return;
  353. }
  354. if (this->IsFull())
  355. {
  356. // If the cache is full, we won't find an empty slot to move the contents of the colliding inline cache to.
  357. return;
  358. }
  359. // Collision is with a cache having a different type.
  360. uint tryInlineCacheIndex = GetNextInlineCacheIndex(inlineCacheIndex);
  361. // Iterate over the inline caches in the polymorphic cache, stop when:
  362. // 1. an empty inline cache is found, or
  363. // 2. a cache already populated with the incoming type is found, or
  364. // 3. all the inline caches have been looked at.
  365. while (!inlineCaches[tryInlineCacheIndex].IsEmpty() && tryInlineCacheIndex != inlineCacheIndex)
  366. {
  367. if (CheckLocal && (inlineCaches[tryInlineCacheIndex].u.local.type == type || inlineCaches[tryInlineCacheIndex].u.local.type == TypeWithAuxSlotTag(type)))
  368. {
  369. break;
  370. }
  371. if (CheckProto && (inlineCaches[tryInlineCacheIndex].u.proto.type == type || inlineCaches[tryInlineCacheIndex].u.proto.type == TypeWithAuxSlotTag(type)))
  372. {
  373. Assert(GetInlineCacheIndexForType(inlineCaches[tryInlineCacheIndex].u.proto.type) == inlineCacheIndex);
  374. break;
  375. }
  376. if (CheckAccessor && (inlineCaches[tryInlineCacheIndex].u.accessor.type == type || inlineCaches[tryInlineCacheIndex].u.accessor.type == TypeWithAuxSlotTag(type)))
  377. {
  378. Assert(GetInlineCacheIndexForType(inlineCaches[tryInlineCacheIndex].u.accessor.type) == inlineCacheIndex);
  379. break;
  380. }
  381. tryInlineCacheIndex = GetNextInlineCacheIndex(tryInlineCacheIndex);
  382. }
  383. if (tryInlineCacheIndex != inlineCacheIndex)
  384. {
  385. if (inlineCaches[inlineCacheIndex].invalidationListSlotPtr != nullptr)
  386. {
  387. Assert(*(inlineCaches[inlineCacheIndex].invalidationListSlotPtr) == &inlineCaches[inlineCacheIndex]);
  388. if (inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr != nullptr)
  389. {
  390. Assert(*(inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr) == &inlineCaches[tryInlineCacheIndex]);
  391. }
  392. else
  393. {
  394. inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr = inlineCaches[inlineCacheIndex].invalidationListSlotPtr;
  395. *(inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr) = &inlineCaches[tryInlineCacheIndex];
  396. inlineCaches[inlineCacheIndex].invalidationListSlotPtr = nullptr;
  397. }
  398. }
  399. inlineCaches[tryInlineCacheIndex].u = inlineCaches[inlineCacheIndex].u;
  400. UpdateInlineCachesFillInfo(tryInlineCacheIndex, true /*set*/);
  401. // Let's clear the cache slot on which we had the collision.
  402. inlineCaches[inlineCacheIndex].RemoveFromInvalidationListAndClear(type->GetScriptContext()->GetThreadContext());
  403. Assert((this->inlineCachesFillInfo & (1 << inlineCacheIndex)) != 0);
  404. UpdateInlineCachesFillInfo(inlineCacheIndex, false /*set*/);
  405. }
  406. }
  407. #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
  408. template <typename TDelegate>
  409. bool PolymorphicInlineCache::CheckClonedInlineCache(uint inlineCacheIndex, TDelegate mapper)
  410. {
  411. bool success = false;
  412. uint tryInlineCacheIndex = GetNextInlineCacheIndex(inlineCacheIndex);
  413. do
  414. {
  415. if (inlineCaches[tryInlineCacheIndex].IsEmpty())
  416. {
  417. break;
  418. }
  419. success = mapper(tryInlineCacheIndex);
  420. if (success)
  421. {
  422. Assert(inlineCaches[inlineCacheIndex].invalidationListSlotPtr == nullptr || *inlineCaches[inlineCacheIndex].invalidationListSlotPtr == &inlineCaches[inlineCacheIndex]);
  423. Assert(inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr == nullptr || *inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr == &inlineCaches[tryInlineCacheIndex]);
  424. // Swap inline caches, including their invalidationListSlotPtrs.
  425. InlineCache temp = inlineCaches[tryInlineCacheIndex];
  426. inlineCaches[tryInlineCacheIndex] = inlineCaches[inlineCacheIndex];
  427. inlineCaches[inlineCacheIndex] = temp;
  428. // Fix up invalidationListSlotPtrs to point to their owners.
  429. if (inlineCaches[inlineCacheIndex].invalidationListSlotPtr != nullptr)
  430. {
  431. *inlineCaches[inlineCacheIndex].invalidationListSlotPtr = &inlineCaches[inlineCacheIndex];
  432. }
  433. if (inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr != nullptr)
  434. {
  435. *inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr = &inlineCaches[tryInlineCacheIndex];
  436. }
  437. break;
  438. }
  439. tryInlineCacheIndex = GetNextInlineCacheIndex(tryInlineCacheIndex);
  440. } while (tryInlineCacheIndex != inlineCacheIndex);
  441. return success;
  442. }
  443. #endif
  444. template<
  445. bool CheckLocal,
  446. bool CheckProto,
  447. bool CheckAccessor,
  448. bool CheckMissing,
  449. bool IsInlineCacheAvailable,
  450. bool ReturnOperationInfo,
  451. bool OutputExistence /*When set, propertyValue is true or false, representing whether the property exists on the instance not its actual value*/>
  452. bool PolymorphicInlineCache::TryGetProperty(
  453. Var const instance,
  454. RecyclableObject *const propertyObject,
  455. const PropertyId propertyId,
  456. Var *const propertyValue,
  457. ScriptContext *const requestContext,
  458. PropertyCacheOperationInfo *const operationInfo,
  459. InlineCache *const inlineCacheToPopulate)
  460. {
  461. Assert(!IsInlineCacheAvailable || inlineCacheToPopulate);
  462. Assert(!ReturnOperationInfo || operationInfo);
  463. Type * const type = propertyObject->GetType();
  464. uint inlineCacheIndex = GetInlineCacheIndexForType(type);
  465. InlineCache *cache = &inlineCaches[inlineCacheIndex];
  466. #ifdef INLINE_CACHE_STATS
  467. bool isEmpty = false;
  468. if (PHASE_STATS1(Js::PolymorphicInlineCachePhase))
  469. {
  470. isEmpty = cache->IsEmpty();
  471. }
  472. #endif
  473. bool result = cache->TryGetProperty<CheckLocal, CheckProto, CheckAccessor, CheckMissing, ReturnOperationInfo, OutputExistence>(
  474. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  475. #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
  476. if (!result && !cache->IsEmpty())
  477. {
  478. result = CheckClonedInlineCache(inlineCacheIndex, [&](uint tryInlineCacheIndex) -> bool
  479. {
  480. cache = &inlineCaches[tryInlineCacheIndex];
  481. return cache->TryGetProperty<CheckLocal, CheckProto, CheckAccessor, CheckMissing, ReturnOperationInfo, OutputExistence>(
  482. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  483. });
  484. }
  485. #endif
  486. if (IsInlineCacheAvailable && result)
  487. {
  488. cache->CopyTo(propertyId, requestContext, inlineCacheToPopulate);
  489. }
  490. #ifdef INLINE_CACHE_STATS
  491. if (PHASE_STATS1(Js::PolymorphicInlineCachePhase))
  492. {
  493. bool collision = !result && !isEmpty;
  494. GetScriptContext()->LogCacheUsage(this, /*isGet*/ true, propertyId, result, collision);
  495. }
  496. #endif
  497. return result;
  498. }
  499. template<
  500. bool CheckLocal,
  501. bool CheckLocalTypeWithoutProperty,
  502. bool CheckAccessor,
  503. bool IsInlineCacheAvailable,
  504. bool ReturnOperationInfo>
  505. bool PolymorphicInlineCache::TrySetProperty(
  506. RecyclableObject *const object,
  507. const PropertyId propertyId,
  508. Var propertyValue,
  509. ScriptContext *const requestContext,
  510. PropertyCacheOperationInfo *const operationInfo,
  511. InlineCache *const inlineCacheToPopulate,
  512. const PropertyOperationFlags propertyOperationFlags)
  513. {
  514. Assert(!IsInlineCacheAvailable || inlineCacheToPopulate);
  515. Assert(!ReturnOperationInfo || operationInfo);
  516. Type * const type = object->GetType();
  517. uint inlineCacheIndex = GetInlineCacheIndexForType(type);
  518. InlineCache *cache = &inlineCaches[inlineCacheIndex];
  519. #ifdef INLINE_CACHE_STATS
  520. bool isEmpty = false;
  521. if (PHASE_STATS1(Js::PolymorphicInlineCachePhase))
  522. {
  523. isEmpty = cache->IsEmpty();
  524. }
  525. #endif
  526. bool result = cache->TrySetProperty<CheckLocal, CheckLocalTypeWithoutProperty, CheckAccessor, ReturnOperationInfo>(
  527. object, propertyId, propertyValue, requestContext, operationInfo, propertyOperationFlags);
  528. #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
  529. if (!result && !cache->IsEmpty())
  530. {
  531. result = CheckClonedInlineCache(inlineCacheIndex, [&](uint tryInlineCacheIndex) -> bool
  532. {
  533. cache = &inlineCaches[tryInlineCacheIndex];
  534. return cache->TrySetProperty<CheckLocal, CheckLocalTypeWithoutProperty, CheckAccessor, ReturnOperationInfo>(
  535. object, propertyId, propertyValue, requestContext, operationInfo, propertyOperationFlags);
  536. });
  537. }
  538. #endif
  539. if (IsInlineCacheAvailable && result)
  540. {
  541. cache->CopyTo(propertyId, requestContext, inlineCacheToPopulate);
  542. }
  543. #ifdef INLINE_CACHE_STATS
  544. if (PHASE_STATS1(Js::PolymorphicInlineCachePhase))
  545. {
  546. bool collision = !result && !isEmpty;
  547. GetScriptContext()->LogCacheUsage(this, /*isGet*/ false, propertyId, result, collision);
  548. }
  549. #endif
  550. return result;
  551. }
  552. }