| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLanguagePch.h"
- namespace Js
- {
- ConstructorCache ConstructorCache::DefaultInstance;
- ConstructorCache::ConstructorCache() : PropertyGuard()
- {
- this->Invalidate();
- this->content.scriptContext = nullptr;
- this->content.slotCount = 0;
- this->content.inlineSlotCount = 0;
- this->content.updateAfterCtor = false;
- this->content.ctorHasNoExplicitReturnValue = false;
- this->content.skipDefaultNewObject = false;
- this->content.isPopulated = false;
- this->content.isPolymorphic = false;
- this->content.typeUpdatePending = false;
- this->content.typeIsFinal = false;
- this->content.hasPrototypeChanged = false;
- this->content.callCount = 0;
- Assert(IsConsistent());
- }
- ConstructorCache::ConstructorCache(ConstructorCache const * other) : PropertyGuard(other->GetValue())
- {
- Assert(other != nullptr);
- this->content.scriptContext = other->content.scriptContext;
- this->content.slotCount = other->content.slotCount;
- this->content.inlineSlotCount = other->content.inlineSlotCount;
- this->content.updateAfterCtor = other->content.updateAfterCtor;
- this->content.ctorHasNoExplicitReturnValue = other->content.ctorHasNoExplicitReturnValue;
- this->content.skipDefaultNewObject = other->content.skipDefaultNewObject;
- this->content.isPopulated = other->content.isPopulated;
- this->content.isPolymorphic = other->content.isPolymorphic;
- this->content.typeUpdatePending = other->content.typeUpdatePending;
- this->content.typeIsFinal = other->content.typeIsFinal;
- this->content.hasPrototypeChanged = other->content.hasPrototypeChanged;
- this->content.callCount = other->content.callCount;
- Assert(IsConsistent());
- }
- void ConstructorCache::Populate(DynamicType* type, ScriptContext* scriptContext, bool ctorHasNoExplicitReturnValue, bool updateAfterCtor)
- {
- Assert(scriptContext == type->GetScriptContext());
- Assert(type->GetIsShared());
- Assert(IsConsistent());
- Assert(!this->content.isPopulated || this->content.isPolymorphic);
- Assert(type->GetTypeHandler()->GetSlotCapacity() <= MaxCachedSlotCount);
- this->content.isPopulated = true;
- this->SetValue((intptr_t)type);
- this->content.scriptContext = scriptContext;
- this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
- this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
- this->content.ctorHasNoExplicitReturnValue = ctorHasNoExplicitReturnValue;
- this->content.updateAfterCtor = updateAfterCtor;
- Assert(IsConsistent());
- }
- void ConstructorCache::PopulateForSkipDefaultNewObject(ScriptContext* scriptContext)
- {
- Assert(IsConsistent());
- Assert(!this->content.isPopulated);
- this->content.isPopulated = true;
- this->SetValue((intptr_t)CtorCacheGuardValues::Special);
- this->content.scriptContext = scriptContext;
- this->content.skipDefaultNewObject = true;
- Assert(IsConsistent());
- }
- bool ConstructorCache::TryUpdateAfterConstructor(DynamicType* type, ScriptContext* scriptContext)
- {
- Assert(scriptContext == type->GetScriptContext());
- Assert(type->GetTypeHandler()->GetMayBecomeShared());
- Assert(IsConsistent());
- Assert(this->content.isPopulated);
- Assert(this->content.scriptContext == scriptContext);
- Assert(!this->content.typeUpdatePending);
- Assert(this->content.ctorHasNoExplicitReturnValue);
- if (type->GetTypeHandler()->GetSlotCapacity() > MaxCachedSlotCount)
- {
- return false;
- }
- if (type->GetIsShared())
- {
- this->SetValue((intptr_t)type);
- this->content.typeIsFinal = true;
- this->content.pendingType = nullptr;
- }
- else
- {
- AssertMsg(false, "No one calls this part of the code?");
- this->SetValue((intptr_t)CtorCacheGuardValues::Special);
- this->content.pendingType = type;
- this->content.typeUpdatePending = true;
- }
- this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
- this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
- Assert(IsConsistent());
- return true;
- }
- void ConstructorCache::UpdateInlineSlotCount()
- {
- Assert(IsConsistent());
- Assert(this->content.isPopulated);
- Assert(IsEnabled() || NeedsTypeUpdate());
- DynamicType* type;
- if (this->content.typeUpdatePending)
- {
- type = this->content.pendingType;
- }
- else
- {
- type = reinterpret_cast<DynamicType*>(this->GetValue());
- }
- DynamicTypeHandler* typeHandler = type->GetTypeHandler();
- // Inline slot capacity should never grow as a result of shrinking.
- Assert(typeHandler->GetInlineSlotCapacity() <= this->content.inlineSlotCount);
- // Slot capacity should never grow as a result of shrinking.
- Assert(typeHandler->GetSlotCapacity() <= this->content.slotCount);
- this->content.slotCount = typeHandler->GetSlotCapacity();
- this->content.inlineSlotCount = typeHandler->GetInlineSlotCapacity();
- Assert(IsConsistent());
- }
- void ConstructorCache::EnableAfterTypeUpdate()
- {
- Assert(IsConsistent());
- Assert(this->content.isPopulated);
- Assert(!IsEnabled());
- Assert((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Special);
- Assert(this->content.typeUpdatePending);
- Assert(this->content.slotCount == this->content.pendingType->GetTypeHandler()->GetSlotCapacity());
- Assert(this->content.inlineSlotCount == this->content.pendingType->GetTypeHandler()->GetInlineSlotCapacity());
- Assert(this->content.pendingType->GetIsShared());
- DynamicType* pendingType = this->content.pendingType;
- this->SetValue((intptr_t)pendingType);
- this->content.typeIsFinal = true;
- this->content.pendingType = nullptr;
- this->content.typeUpdatePending = false;
- Assert(IsConsistent());
- }
- ConstructorCache* ConstructorCache::EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext)
- {
- Assert(currentCache != nullptr);
- ConstructorCache* newCache = currentCache;
- // If the old cache has been invalidated, we need to create a new one to avoid incorrectly re-validating
- // caches that may have been hard-coded in the JIT-ed code with different prototype and type. However, if
- // the cache is already polymorphic, it will not be hard-coded, and hence we don't need to allocate a new
- // one - in case the prototype property changes frequently.
- if (ConstructorCache::IsDefault(currentCache) || (currentCache->IsInvalidated() && !currentCache->IsPolymorphic()))
- {
- // Review (jedmiad): I don't think we need to zero the struct, since we initialize each field.
- newCache = RecyclerNew(scriptContext->GetRecycler(), ConstructorCache);
- // TODO: Consider marking the cache as polymorphic only if the prototype and type actually changed. In fact,
- // if they didn't change we could reuse the same cache and simply mark it as valid. Not really true. The cache
- // might have been invalidated due to a property becoming read-only. In that case we can't re-validate an old
- // monomorphic cache. We must allocate a new one.
- newCache->content.isPolymorphic = currentCache->content.isPopulated && currentCache->content.hasPrototypeChanged;
- }
- // If we kept the old invalidated cache, it better be marked as polymorphic.
- Assert(!newCache->IsInvalidated() || newCache->IsPolymorphic());
- // If the cache was polymorphic, we shouldn't have allocated a new one.
- Assert(!currentCache->IsPolymorphic() || newCache == currentCache);
- return newCache;
- }
- void ConstructorCache::InvalidateOnPrototypeChange()
- {
- if (IsDefault(this))
- {
- Assert((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Invalid);
- Assert(!this->content.isPopulated);
- }
- else if ((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject)
- {
- // Do nothing. If we skip the default object, changes to the prototype property don't affect
- // what we'll do during object allocation.
- // Can't assert the following because we set the prototype property during library initialization.
- // AssertMsg(false, "Overriding a prototype on a built-in constructor should be illegal.");
- }
- else
- {
- this->Invalidate();
- this->content.hasPrototypeChanged = true;
- // Make sure we don't leak the old type.
- this->content.pendingType = nullptr;
- Assert(this->content.pendingType == nullptr);
- Assert(IsInvalidated());
- }
- Assert(IsConsistent());
- }
- #if DBG_DUMP
- void ConstructorCache::Dump() const
- {
- 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"),
- this->GetRawGuardValue(), this->GetScriptContext(), this->GetPendingType(), this->GetSlotCount(), this->GetInlineSlotCount(),
- this->IsPopulated(), this->IsPolymorphic(), this->GetUpdateCacheAfterCtor(), this->GetTypeUpdatePending(),
- this->GetSkipDefaultNewObject(), this->GetCtorHasNoExplicitReturnValue());
- }
- #endif
- }
|