InlineCache.inl 30 KB

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