ConstructorCache.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. #include "RuntimeLanguagePch.h"
  6. namespace Js
  7. {
  8. ConstructorCache ConstructorCache::DefaultInstance;
  9. ConstructorCache::ConstructorCache() : PropertyGuard()
  10. {
  11. this->Invalidate();
  12. this->content.scriptContext = nullptr;
  13. this->content.slotCount = 0;
  14. this->content.inlineSlotCount = 0;
  15. this->content.updateAfterCtor = false;
  16. this->content.ctorHasNoExplicitReturnValue = false;
  17. this->content.skipDefaultNewObject = false;
  18. this->content.isPopulated = false;
  19. this->content.isPolymorphic = false;
  20. this->content.typeUpdatePending = false;
  21. this->content.typeIsFinal = false;
  22. this->content.hasPrototypeChanged = false;
  23. this->content.callCount = 0;
  24. Assert(IsConsistent());
  25. }
  26. ConstructorCache::ConstructorCache(ConstructorCache const * other) : PropertyGuard(other->GetValue())
  27. {
  28. Assert(other != nullptr);
  29. this->content.scriptContext = other->content.scriptContext;
  30. this->content.slotCount = other->content.slotCount;
  31. this->content.inlineSlotCount = other->content.inlineSlotCount;
  32. this->content.updateAfterCtor = other->content.updateAfterCtor;
  33. this->content.ctorHasNoExplicitReturnValue = other->content.ctorHasNoExplicitReturnValue;
  34. this->content.skipDefaultNewObject = other->content.skipDefaultNewObject;
  35. this->content.isPopulated = other->content.isPopulated;
  36. this->content.isPolymorphic = other->content.isPolymorphic;
  37. this->content.typeUpdatePending = other->content.typeUpdatePending;
  38. this->content.typeIsFinal = other->content.typeIsFinal;
  39. this->content.hasPrototypeChanged = other->content.hasPrototypeChanged;
  40. this->content.callCount = other->content.callCount;
  41. Assert(IsConsistent());
  42. }
  43. void ConstructorCache::Populate(DynamicType* type, ScriptContext* scriptContext, bool ctorHasNoExplicitReturnValue, bool updateAfterCtor)
  44. {
  45. Assert(scriptContext == type->GetScriptContext());
  46. Assert(type->GetIsShared());
  47. Assert(IsConsistent());
  48. Assert(!this->content.isPopulated || this->content.isPolymorphic);
  49. Assert(type->GetTypeHandler()->GetSlotCapacity() <= MaxCachedSlotCount);
  50. this->content.isPopulated = true;
  51. this->SetValue((intptr_t)type);
  52. this->content.scriptContext = scriptContext;
  53. this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
  54. this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
  55. this->content.ctorHasNoExplicitReturnValue = ctorHasNoExplicitReturnValue;
  56. this->content.updateAfterCtor = updateAfterCtor;
  57. Assert(IsConsistent());
  58. }
  59. void ConstructorCache::PopulateForSkipDefaultNewObject(ScriptContext* scriptContext)
  60. {
  61. Assert(IsConsistent());
  62. Assert(!this->content.isPopulated);
  63. this->content.isPopulated = true;
  64. this->SetValue((intptr_t)CtorCacheGuardValues::Special);
  65. this->content.scriptContext = scriptContext;
  66. this->content.skipDefaultNewObject = true;
  67. Assert(IsConsistent());
  68. }
  69. bool ConstructorCache::TryUpdateAfterConstructor(DynamicType* type, ScriptContext* scriptContext)
  70. {
  71. Assert(scriptContext == type->GetScriptContext());
  72. Assert(type->GetTypeHandler()->GetMayBecomeShared());
  73. Assert(IsConsistent());
  74. Assert(this->content.isPopulated);
  75. Assert(this->content.scriptContext == scriptContext);
  76. Assert(!this->content.typeUpdatePending);
  77. Assert(this->content.ctorHasNoExplicitReturnValue);
  78. if (type->GetTypeHandler()->GetSlotCapacity() > MaxCachedSlotCount)
  79. {
  80. return false;
  81. }
  82. if (type->GetIsShared())
  83. {
  84. this->SetValue((intptr_t)type);
  85. this->content.typeIsFinal = true;
  86. this->content.pendingType = nullptr;
  87. }
  88. else
  89. {
  90. AssertMsg(false, "No one calls this part of the code?");
  91. this->SetValue((intptr_t)CtorCacheGuardValues::Special);
  92. this->content.pendingType = type;
  93. this->content.typeUpdatePending = true;
  94. }
  95. this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
  96. this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
  97. Assert(IsConsistent());
  98. return true;
  99. }
  100. void ConstructorCache::UpdateInlineSlotCount()
  101. {
  102. Assert(IsConsistent());
  103. Assert(this->content.isPopulated);
  104. Assert(IsEnabled() || NeedsTypeUpdate());
  105. DynamicType* type;
  106. if (this->content.typeUpdatePending)
  107. {
  108. type = this->content.pendingType;
  109. }
  110. else
  111. {
  112. type = reinterpret_cast<DynamicType*>(this->GetValue());
  113. }
  114. DynamicTypeHandler* typeHandler = type->GetTypeHandler();
  115. // Inline slot capacity should never grow as a result of shrinking.
  116. Assert(typeHandler->GetInlineSlotCapacity() <= this->content.inlineSlotCount);
  117. // Slot capacity should never grow as a result of shrinking.
  118. Assert(typeHandler->GetSlotCapacity() <= this->content.slotCount);
  119. this->content.slotCount = typeHandler->GetSlotCapacity();
  120. this->content.inlineSlotCount = typeHandler->GetInlineSlotCapacity();
  121. Assert(IsConsistent());
  122. }
  123. void ConstructorCache::EnableAfterTypeUpdate()
  124. {
  125. Assert(IsConsistent());
  126. Assert(this->content.isPopulated);
  127. Assert(!IsEnabled());
  128. Assert((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Special);
  129. Assert(this->content.typeUpdatePending);
  130. Assert(this->content.slotCount == this->content.pendingType->GetTypeHandler()->GetSlotCapacity());
  131. Assert(this->content.inlineSlotCount == this->content.pendingType->GetTypeHandler()->GetInlineSlotCapacity());
  132. Assert(this->content.pendingType->GetIsShared());
  133. DynamicType* pendingType = this->content.pendingType;
  134. this->SetValue((intptr_t)pendingType);
  135. this->content.typeIsFinal = true;
  136. this->content.pendingType = nullptr;
  137. this->content.typeUpdatePending = false;
  138. Assert(IsConsistent());
  139. }
  140. ConstructorCache* ConstructorCache::EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext)
  141. {
  142. Assert(currentCache != nullptr);
  143. ConstructorCache* newCache = currentCache;
  144. // If the old cache has been invalidated, we need to create a new one to avoid incorrectly re-validating
  145. // caches that may have been hard-coded in the JIT-ed code with different prototype and type. However, if
  146. // the cache is already polymorphic, it will not be hard-coded, and hence we don't need to allocate a new
  147. // one - in case the prototype property changes frequently.
  148. if (ConstructorCache::IsDefault(currentCache) || (currentCache->IsInvalidated() && !currentCache->IsPolymorphic()))
  149. {
  150. // Review (jedmiad): I don't think we need to zero the struct, since we initialize each field.
  151. newCache = RecyclerNew(scriptContext->GetRecycler(), ConstructorCache);
  152. // TODO: Consider marking the cache as polymorphic only if the prototype and type actually changed. In fact,
  153. // if they didn't change we could reuse the same cache and simply mark it as valid. Not really true. The cache
  154. // might have been invalidated due to a property becoming read-only. In that case we can't re-validate an old
  155. // monomorphic cache. We must allocate a new one.
  156. newCache->content.isPolymorphic = currentCache->content.isPopulated && currentCache->content.hasPrototypeChanged;
  157. }
  158. // If we kept the old invalidated cache, it better be marked as polymorphic.
  159. Assert(!newCache->IsInvalidated() || newCache->IsPolymorphic());
  160. // If the cache was polymorphic, we shouldn't have allocated a new one.
  161. Assert(!currentCache->IsPolymorphic() || newCache == currentCache);
  162. return newCache;
  163. }
  164. void ConstructorCache::InvalidateOnPrototypeChange()
  165. {
  166. if (IsDefault(this))
  167. {
  168. Assert((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Invalid);
  169. Assert(!this->content.isPopulated);
  170. }
  171. else if ((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject)
  172. {
  173. // Do nothing. If we skip the default object, changes to the prototype property don't affect
  174. // what we'll do during object allocation.
  175. // Can't assert the following because we set the prototype property during library initialization.
  176. // AssertMsg(false, "Overriding a prototype on a built-in constructor should be illegal.");
  177. }
  178. else
  179. {
  180. this->Invalidate();
  181. this->content.hasPrototypeChanged = true;
  182. // Make sure we don't leak the old type.
  183. this->content.pendingType = nullptr;
  184. Assert(this->content.pendingType == nullptr);
  185. Assert(IsInvalidated());
  186. }
  187. Assert(IsConsistent());
  188. }
  189. #if DBG_DUMP
  190. void ConstructorCache::Dump() const
  191. {
  192. Output::Print(_u("guard value or type = 0x%p, script context = 0x%p, pending type = 0x%p, slots = %d, inline slots = %d, populated = %d, polymorphic = %d, update cache = %d, update type = %d, skip default = %d, no return = %d"),
  193. this->GetRawGuardValue(), this->GetScriptContext(), this->GetPendingType(), this->GetSlotCount(), this->GetInlineSlotCount(),
  194. this->IsPopulated(), this->IsPolymorphic(), this->GetUpdateCacheAfterCtor(), this->GetTypeUpdatePending(),
  195. this->GetSkipDefaultNewObject(), this->GetCtorHasNoExplicitReturnValue());
  196. }
  197. #endif
  198. }