InlineCache.inl 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  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 UnsafeVarTo<DynamicObject>(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
  165. // code Save old disableImplicitFlags and implicitCallFlags and disable implicit call and exception.
  166. ThreadContext * threadContext = requestContext->GetThreadContext();
  167. ThreadContext::AutoRestoreImplicitFlags autoRestoreImplicitFlags(threadContext, threadContext->GetImplicitCallFlags(), threadContext->GetDisableImplicitFlags());
  168. threadContext->ClearImplicitCallFlags();
  169. threadContext->SetDisableImplicitFlags(DisableImplicitCallAndExceptionFlag);
  170. DescriptorFlags flags = DescriptorFlags::None;
  171. canSetField = !JavascriptOperators::CheckPrototypesForAccessorOrNonWritablePropertySlow(object, propertyId, &setterValue, &flags, isRoot, requestContext);
  172. if (threadContext->GetImplicitCallFlags() != Js::ImplicitCall_None)
  173. {
  174. canSetField = true; // If there was an implicit call, inconclusive. Disable debug check.
  175. setterValue = nullptr;
  176. }
  177. else if ((flags & Accessor) == Accessor)
  178. {
  179. Assert(setterValue != nullptr);
  180. }
  181. // ImplicitCallFlags and DisableImplicitFlags restored by AutoRestoreImplicitFlags' destructor.
  182. }
  183. #endif
  184. Type *const type = object->GetType();
  185. if (CheckLocal && type == u.local.type)
  186. {
  187. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  188. Assert(isRoot || object->GetPropertyIndex(propertyId) == VarTo<DynamicObject>(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(u.local.slotIndex, true));
  189. Assert(!isRoot || VarTo<RootObjectBase>(object)->GetRootPropertyIndex(propertyId) == VarTo<DynamicObject>(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(u.local.slotIndex, true));
  190. Assert(object->CanStorePropertyValueDirectly(propertyId, isRoot));
  191. UnsafeVarTo<DynamicObject>(object)->SetInlineSlot(SetSlotArgumentsRoot(propertyId, isRoot, u.local.slotIndex, propertyValue));
  192. if (ReturnOperationInfo)
  193. {
  194. operationInfo->cacheType = CacheType_Local;
  195. operationInfo->slotType = SlotType_Inline;
  196. }
  197. Assert(canSetField);
  198. return true;
  199. }
  200. if (CheckLocal && TypeWithAuxSlotTag(type) == u.local.type)
  201. {
  202. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  203. Assert(isRoot || object->GetPropertyIndex(propertyId) == VarTo<DynamicObject>(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(u.local.slotIndex, false));
  204. Assert(!isRoot || VarTo<RootObjectBase>(object)->GetRootPropertyIndex(propertyId) == VarTo<DynamicObject>(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(u.local.slotIndex, false));
  205. Assert(object->CanStorePropertyValueDirectly(propertyId, isRoot));
  206. UnsafeVarTo<DynamicObject>(object)->SetAuxSlot(SetSlotArgumentsRoot(propertyId, isRoot, u.local.slotIndex, propertyValue));
  207. if (ReturnOperationInfo)
  208. {
  209. operationInfo->cacheType = CacheType_Local;
  210. operationInfo->slotType = SlotType_Aux;
  211. }
  212. Assert(canSetField);
  213. return true;
  214. }
  215. if (CheckLocalTypeWithoutProperty && type == u.local.typeWithoutProperty)
  216. {
  217. // CAREFUL! CheckIfPrototypeChainHasOnlyWritableDataProperties may do allocation that triggers GC and
  218. // clears this cache, so save any info that is needed from the cache before calling those functions.
  219. Type *const typeWithProperty = u.local.type;
  220. const PropertyIndex propertyIndex = u.local.slotIndex;
  221. #if DBG
  222. uint16 newAuxSlotCapacity = u.local.requiredAuxSlotCapacity;
  223. #endif
  224. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  225. Assert(typeWithProperty);
  226. Assert(DynamicType::Is(typeWithProperty));
  227. Assert(((DynamicType*)typeWithProperty)->GetIsShared());
  228. Assert(((DynamicType*)typeWithProperty)->GetTypeHandler()->IsPathTypeHandler());
  229. AssertMsg(!((DynamicType*)u.local.typeWithoutProperty)->GetTypeHandler()->GetIsPrototype(), "Why did we cache a property add for a prototype?");
  230. Assert(((DynamicType*)typeWithProperty)->GetTypeHandler()->CanStorePropertyValueDirectly((const DynamicObject*)object, propertyId, isRoot));
  231. DynamicObject *const dynamicObject = UnsafeVarTo<DynamicObject>(object);
  232. // If we're adding a property to an inlined slot, we should never need to adjust auxiliary slot array size.
  233. Assert(newAuxSlotCapacity == 0);
  234. dynamicObject->type = typeWithProperty;
  235. Assert(isRoot || object->GetPropertyIndex(propertyId) == VarTo<DynamicObject>(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(propertyIndex, true));
  236. Assert(!isRoot || VarTo<RootObjectBase>(object)->GetRootPropertyIndex(propertyId) == VarTo<DynamicObject>(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(propertyIndex, true));
  237. dynamicObject->SetInlineSlot(SetSlotArgumentsRoot(propertyId, isRoot, propertyIndex, propertyValue));
  238. if (ReturnOperationInfo)
  239. {
  240. operationInfo->cacheType = CacheType_LocalWithoutProperty;
  241. operationInfo->slotType = SlotType_Inline;
  242. }
  243. return true;
  244. }
  245. if (CheckLocalTypeWithoutProperty && TypeWithAuxSlotTag(type) == u.local.typeWithoutProperty)
  246. {
  247. // CAREFUL! CheckIfPrototypeChainHasOnlyWritableDataProperties or AdjustSlots may do allocation that triggers GC and
  248. // clears this cache, so save any info that is needed from the cache before calling those functions.
  249. Type *const typeWithProperty = TypeWithoutAuxSlotTag(u.local.type);
  250. const PropertyIndex propertyIndex = u.local.slotIndex;
  251. uint16 newAuxSlotCapacity = u.local.requiredAuxSlotCapacity;
  252. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  253. Assert(typeWithProperty);
  254. Assert(DynamicType::Is(typeWithProperty));
  255. Assert(((DynamicType*)typeWithProperty)->GetIsShared());
  256. Assert(((DynamicType*)typeWithProperty)->GetTypeHandler()->IsPathTypeHandler());
  257. AssertMsg(!((DynamicType*)TypeWithoutAuxSlotTag(u.local.typeWithoutProperty))->GetTypeHandler()->GetIsPrototype(), "Why did we cache a property add for a prototype?");
  258. Assert(((DynamicType*)typeWithProperty)->GetTypeHandler()->CanStorePropertyValueDirectly((const DynamicObject*)object, propertyId, isRoot));
  259. DynamicObject *const dynamicObject = UnsafeVarTo<DynamicObject>(object);
  260. if (newAuxSlotCapacity > 0)
  261. {
  262. DynamicTypeHandler::AdjustSlots(
  263. dynamicObject,
  264. static_cast<DynamicType *>(typeWithProperty)->GetTypeHandler()->GetInlineSlotCapacity(),
  265. newAuxSlotCapacity);
  266. }
  267. dynamicObject->type = typeWithProperty;
  268. Assert(isRoot || object->GetPropertyIndex(propertyId) == VarTo<DynamicObject>(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(propertyIndex, false));
  269. Assert(!isRoot || VarTo<RootObjectBase>(object)->GetRootPropertyIndex(propertyId) == VarTo<DynamicObject>(object)->GetTypeHandler()->InlineOrAuxSlotIndexToPropertyIndex(propertyIndex, false));
  270. dynamicObject->SetAuxSlot(SetSlotArgumentsRoot(propertyId, isRoot, propertyIndex, propertyValue));
  271. if (ReturnOperationInfo)
  272. {
  273. operationInfo->cacheType = CacheType_LocalWithoutProperty;
  274. operationInfo->slotType = SlotType_Aux;
  275. }
  276. return true;
  277. }
  278. if (CheckAccessor && type == u.accessor.type)
  279. {
  280. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  281. Assert(u.accessor.flags & InlineCacheSetterFlag);
  282. RecyclableObject * function;
  283. if (u.accessor.isOnProto)
  284. {
  285. function = UnsafeVarTo<RecyclableObject>(u.accessor.object->GetInlineSlot(u.accessor.slotIndex));
  286. }
  287. else
  288. {
  289. function = UnsafeVarTo<RecyclableObject>(VarTo<DynamicObject>(object)->GetInlineSlot(u.accessor.slotIndex));
  290. }
  291. Assert(setterValue == nullptr || setterValue == function);
  292. if (!JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, function, requestContext) &&
  293. !JavascriptError::ThrowIfNotExtensibleUndefinedSetter(propertyOperationFlags, function, requestContext))
  294. {
  295. Js::JavascriptOperators::CallSetter(function, object, propertyValue, requestContext);
  296. }
  297. if (ReturnOperationInfo)
  298. {
  299. operationInfo->cacheType = CacheType_Setter;
  300. operationInfo->slotType = SlotType_Inline;
  301. }
  302. return true;
  303. }
  304. if (CheckAccessor && TypeWithAuxSlotTag(type) == u.accessor.type)
  305. {
  306. Assert(object->GetScriptContext() == requestContext); // we never cache a type from another script context
  307. Assert(u.accessor.flags & InlineCacheSetterFlag);
  308. RecyclableObject * function;
  309. if (u.accessor.isOnProto)
  310. {
  311. function = UnsafeVarTo<RecyclableObject>(u.accessor.object->GetAuxSlot(u.accessor.slotIndex));
  312. }
  313. else
  314. {
  315. function = UnsafeVarTo<RecyclableObject>(VarTo<DynamicObject>(object)->GetAuxSlot(u.accessor.slotIndex));
  316. }
  317. Assert(setterValue == nullptr || setterValue == function);
  318. if (!JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, function, requestContext) &&
  319. !JavascriptError::ThrowIfNotExtensibleUndefinedSetter(propertyOperationFlags, function, requestContext))
  320. {
  321. Js::JavascriptOperators::CallSetter(function, object, propertyValue, requestContext);
  322. }
  323. if (ReturnOperationInfo)
  324. {
  325. operationInfo->cacheType = CacheType_Setter;
  326. operationInfo->slotType = SlotType_Aux;
  327. }
  328. return true;
  329. }
  330. return false;
  331. }
  332. template<
  333. bool CheckLocal,
  334. bool CheckProto,
  335. bool CheckAccessor>
  336. void PolymorphicInlineCache::CloneInlineCacheToEmptySlotInCollision(Type * const type, uint inlineCacheIndex)
  337. {
  338. if (CheckLocal && (inlineCaches[inlineCacheIndex].u.local.type == type || inlineCaches[inlineCacheIndex].u.local.type == TypeWithAuxSlotTag(type)))
  339. {
  340. return;
  341. }
  342. if (CheckProto && (inlineCaches[inlineCacheIndex].u.proto.type == type || inlineCaches[inlineCacheIndex].u.proto.type == TypeWithAuxSlotTag(type)))
  343. {
  344. return;
  345. }
  346. if (CheckAccessor && (inlineCaches[inlineCacheIndex].u.accessor.type == type || inlineCaches[inlineCacheIndex].u.accessor.type == TypeWithAuxSlotTag(type)))
  347. {
  348. return;
  349. }
  350. if (this->IsFull())
  351. {
  352. // If the cache is full, we won't find an empty slot to move the contents of the colliding inline cache to.
  353. return;
  354. }
  355. // Collision is with a cache having a different type.
  356. uint tryInlineCacheIndex = GetNextInlineCacheIndex(inlineCacheIndex);
  357. // Iterate over the inline caches in the polymorphic cache, stop when:
  358. // 1. an empty inline cache is found, or
  359. // 2. a cache already populated with the incoming type is found, or
  360. // 3. all the inline caches have been looked at.
  361. while (!inlineCaches[tryInlineCacheIndex].IsEmpty() && tryInlineCacheIndex != inlineCacheIndex)
  362. {
  363. if (CheckLocal && (inlineCaches[tryInlineCacheIndex].u.local.type == type || inlineCaches[tryInlineCacheIndex].u.local.type == TypeWithAuxSlotTag(type)))
  364. {
  365. break;
  366. }
  367. if (CheckProto && (inlineCaches[tryInlineCacheIndex].u.proto.type == type || inlineCaches[tryInlineCacheIndex].u.proto.type == TypeWithAuxSlotTag(type)))
  368. {
  369. Assert(GetInlineCacheIndexForType(inlineCaches[tryInlineCacheIndex].u.proto.type) == inlineCacheIndex);
  370. break;
  371. }
  372. if (CheckAccessor && (inlineCaches[tryInlineCacheIndex].u.accessor.type == type || inlineCaches[tryInlineCacheIndex].u.accessor.type == TypeWithAuxSlotTag(type)))
  373. {
  374. Assert(GetInlineCacheIndexForType(inlineCaches[tryInlineCacheIndex].u.accessor.type) == inlineCacheIndex);
  375. break;
  376. }
  377. tryInlineCacheIndex = GetNextInlineCacheIndex(tryInlineCacheIndex);
  378. }
  379. if (tryInlineCacheIndex != inlineCacheIndex)
  380. {
  381. if (inlineCaches[inlineCacheIndex].invalidationListSlotPtr != nullptr)
  382. {
  383. Assert(*(inlineCaches[inlineCacheIndex].invalidationListSlotPtr) == &inlineCaches[inlineCacheIndex]);
  384. if (inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr != nullptr)
  385. {
  386. Assert(*(inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr) == &inlineCaches[tryInlineCacheIndex]);
  387. }
  388. else
  389. {
  390. inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr = inlineCaches[inlineCacheIndex].invalidationListSlotPtr;
  391. *(inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr) = &inlineCaches[tryInlineCacheIndex];
  392. inlineCaches[inlineCacheIndex].invalidationListSlotPtr = nullptr;
  393. }
  394. }
  395. inlineCaches[tryInlineCacheIndex].u = inlineCaches[inlineCacheIndex].u;
  396. UpdateInlineCachesFillInfo(tryInlineCacheIndex, true /*set*/);
  397. // Let's clear the cache slot on which we had the collision.
  398. inlineCaches[inlineCacheIndex].RemoveFromInvalidationListAndClear(type->GetScriptContext()->GetThreadContext());
  399. Assert((this->inlineCachesFillInfo & (1 << inlineCacheIndex)) != 0);
  400. UpdateInlineCachesFillInfo(inlineCacheIndex, false /*set*/);
  401. }
  402. }
  403. #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
  404. template <typename TDelegate>
  405. bool PolymorphicInlineCache::CheckClonedInlineCache(uint inlineCacheIndex, TDelegate mapper)
  406. {
  407. bool success = false;
  408. uint tryInlineCacheIndex = GetNextInlineCacheIndex(inlineCacheIndex);
  409. do
  410. {
  411. if (inlineCaches[tryInlineCacheIndex].IsEmpty())
  412. {
  413. break;
  414. }
  415. success = mapper(tryInlineCacheIndex);
  416. if (success)
  417. {
  418. Assert(inlineCaches[inlineCacheIndex].invalidationListSlotPtr == nullptr || *inlineCaches[inlineCacheIndex].invalidationListSlotPtr == &inlineCaches[inlineCacheIndex]);
  419. Assert(inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr == nullptr || *inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr == &inlineCaches[tryInlineCacheIndex]);
  420. // Swap inline caches, including their invalidationListSlotPtrs.
  421. InlineCache temp = inlineCaches[tryInlineCacheIndex];
  422. inlineCaches[tryInlineCacheIndex] = inlineCaches[inlineCacheIndex];
  423. inlineCaches[inlineCacheIndex] = temp;
  424. // Fix up invalidationListSlotPtrs to point to their owners.
  425. if (inlineCaches[inlineCacheIndex].invalidationListSlotPtr != nullptr)
  426. {
  427. *inlineCaches[inlineCacheIndex].invalidationListSlotPtr = &inlineCaches[inlineCacheIndex];
  428. }
  429. if (inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr != nullptr)
  430. {
  431. *inlineCaches[tryInlineCacheIndex].invalidationListSlotPtr = &inlineCaches[tryInlineCacheIndex];
  432. }
  433. break;
  434. }
  435. tryInlineCacheIndex = GetNextInlineCacheIndex(tryInlineCacheIndex);
  436. } while (tryInlineCacheIndex != inlineCacheIndex);
  437. return success;
  438. }
  439. #endif
  440. template<
  441. bool CheckLocal,
  442. bool CheckProto,
  443. bool CheckAccessor,
  444. bool CheckMissing,
  445. bool IsInlineCacheAvailable,
  446. bool ReturnOperationInfo,
  447. bool OutputExistence /*When set, propertyValue is true or false, representing whether the property exists on the instance not its actual value*/>
  448. bool PolymorphicInlineCache::TryGetProperty(
  449. Var const instance,
  450. RecyclableObject *const propertyObject,
  451. const PropertyId propertyId,
  452. Var *const propertyValue,
  453. ScriptContext *const requestContext,
  454. PropertyCacheOperationInfo *const operationInfo,
  455. InlineCache *const inlineCacheToPopulate)
  456. {
  457. Assert(!IsInlineCacheAvailable || inlineCacheToPopulate);
  458. Assert(!ReturnOperationInfo || operationInfo);
  459. Type * const type = propertyObject->GetType();
  460. uint inlineCacheIndex = GetInlineCacheIndexForType(type);
  461. InlineCache *cache = &inlineCaches[inlineCacheIndex];
  462. #ifdef INLINE_CACHE_STATS
  463. bool isEmpty = false;
  464. if (PHASE_STATS1(Js::PolymorphicInlineCachePhase))
  465. {
  466. isEmpty = cache->IsEmpty();
  467. }
  468. #endif
  469. bool result = cache->TryGetProperty<CheckLocal, CheckProto, CheckAccessor, CheckMissing, ReturnOperationInfo, OutputExistence>(
  470. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  471. #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
  472. if (!result && !cache->IsEmpty())
  473. {
  474. result = CheckClonedInlineCache(inlineCacheIndex, [&](uint tryInlineCacheIndex) -> bool
  475. {
  476. cache = &inlineCaches[tryInlineCacheIndex];
  477. return cache->TryGetProperty<CheckLocal, CheckProto, CheckAccessor, CheckMissing, ReturnOperationInfo, OutputExistence>(
  478. instance, propertyObject, propertyId, propertyValue, requestContext, operationInfo);
  479. });
  480. }
  481. #endif
  482. if (IsInlineCacheAvailable && result)
  483. {
  484. cache->CopyTo(propertyId, requestContext, inlineCacheToPopulate);
  485. }
  486. #ifdef INLINE_CACHE_STATS
  487. if (PHASE_STATS1(Js::PolymorphicInlineCachePhase))
  488. {
  489. bool collision = !result && !isEmpty;
  490. GetScriptContext()->LogCacheUsage(this, /*isGet*/ true, propertyId, result, collision);
  491. }
  492. #endif
  493. return result;
  494. }
  495. template<
  496. bool CheckLocal,
  497. bool CheckLocalTypeWithoutProperty,
  498. bool CheckAccessor,
  499. bool IsInlineCacheAvailable,
  500. bool ReturnOperationInfo>
  501. bool PolymorphicInlineCache::TrySetProperty(
  502. RecyclableObject *const object,
  503. const PropertyId propertyId,
  504. Var propertyValue,
  505. ScriptContext *const requestContext,
  506. PropertyCacheOperationInfo *const operationInfo,
  507. InlineCache *const inlineCacheToPopulate,
  508. const PropertyOperationFlags propertyOperationFlags)
  509. {
  510. Assert(!IsInlineCacheAvailable || inlineCacheToPopulate);
  511. Assert(!ReturnOperationInfo || operationInfo);
  512. Type * const type = object->GetType();
  513. uint inlineCacheIndex = GetInlineCacheIndexForType(type);
  514. InlineCache *cache = &inlineCaches[inlineCacheIndex];
  515. #ifdef INLINE_CACHE_STATS
  516. bool isEmpty = false;
  517. if (PHASE_STATS1(Js::PolymorphicInlineCachePhase))
  518. {
  519. isEmpty = cache->IsEmpty();
  520. }
  521. #endif
  522. bool result = cache->TrySetProperty<CheckLocal, CheckLocalTypeWithoutProperty, CheckAccessor, ReturnOperationInfo>(
  523. object, propertyId, propertyValue, requestContext, operationInfo, propertyOperationFlags);
  524. #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
  525. if (!result && !cache->IsEmpty())
  526. {
  527. result = CheckClonedInlineCache(inlineCacheIndex, [&](uint tryInlineCacheIndex) -> bool
  528. {
  529. cache = &inlineCaches[tryInlineCacheIndex];
  530. return cache->TrySetProperty<CheckLocal, CheckLocalTypeWithoutProperty, CheckAccessor, ReturnOperationInfo>(
  531. object, propertyId, propertyValue, requestContext, operationInfo, propertyOperationFlags);
  532. });
  533. }
  534. #endif
  535. if (IsInlineCacheAvailable && result)
  536. {
  537. cache->CopyTo(propertyId, requestContext, inlineCacheToPopulate);
  538. }
  539. #ifdef INLINE_CACHE_STATS
  540. if (PHASE_STATS1(Js::PolymorphicInlineCachePhase))
  541. {
  542. bool collision = !result && !isEmpty;
  543. GetScriptContext()->LogCacheUsage(this, /*isGet*/ false, propertyId, result, collision);
  544. }
  545. #endif
  546. return result;
  547. }
  548. }