| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #pragma once
- #define TypeWithAuxSlotTag(_t) \
- (reinterpret_cast<Type*>(reinterpret_cast<size_t>(_t) | InlineCacheAuxSlotTypeTag))
- #define TypeWithoutAuxSlotTag(_t) \
- (reinterpret_cast<Js::Type*>(reinterpret_cast<size_t>(_t) & ~InlineCacheAuxSlotTypeTag))
- #define TypeHasAuxSlotTag(_t) \
- (!!(reinterpret_cast<size_t>(_t) & InlineCacheAuxSlotTypeTag))
- #if defined(_M_IX86_OR_ARM32)
- #define PolymorphicInlineCacheShift 5 // On 32 bit architectures, the least 5 significant bits of a DynamicTypePointer is 0
- #else
- #define PolymorphicInlineCacheShift 6 // On 64 bit architectures, the least 6 significant bits of a DynamicTypePointer is 0
- #endif
- // TODO: OOP JIT, move equiv set to backend?
- // forward decl
- class JITType;
- template <class TAllocator> class JITTypeHolderBase;
- typedef JITTypeHolderBase<void> JITTypeHolder;
- typedef JITTypeHolderBase<Recycler> RecyclerJITTypeHolder;
- namespace Js
- {
- enum CacheType : byte
- {
- CacheType_None,
- CacheType_Local,
- CacheType_Proto,
- CacheType_LocalWithoutProperty,
- CacheType_Getter,
- CacheType_Setter,
- CacheType_TypeProperty,
- };
- enum SlotType : byte
- {
- SlotType_None,
- SlotType_Inline,
- SlotType_Aux,
- };
- struct PropertyCacheOperationInfo
- {
- PropertyCacheOperationInfo()
- : cacheType(CacheType_None), slotType(SlotType_None), isPolymorphic(false)
- {
- }
- CacheType cacheType;
- SlotType slotType;
- bool isPolymorphic;
- };
- struct JitTimeInlineCache;
- struct InlineCache
- {
- static const int CacheLayoutSelectorBitCount = 1;
- static const int RequiredAuxSlotCapacityBitCount = 15;
- static const bool IsPolymorphic = false;
- InlineCache() {}
- union
- {
- // Invariants:
- // - Type* fields do not overlap.
- // - "next" field is non-null iff the cache is linked in a list of proto-caches
- // (see ScriptContext::RegisterProtoInlineCache and ScriptContext::InvalidateProtoCaches).
- struct s_local
- {
- Type* type;
- // PatchPutValue caches here the type the object has before a new property is added.
- // If this type is hit again we can immediately change the object's type to "type"
- // and store the value into the slot "slotIndex".
- Type* typeWithoutProperty;
- union
- {
- struct
- {
- uint16 isLocal : 1;
- uint16 requiredAuxSlotCapacity : 15; // Maximum auxiliary slot capacity (for a path type) must be < 2^16
- };
- struct
- {
- uint16 rawUInt16; // Required for access from JIT-ed code
- };
- };
- uint16 slotIndex;
- } local;
- struct s_proto
- {
- uint16 isProto : 1;
- uint16 isMissing : 1;
- uint16 unused : 14;
- uint16 slotIndex;
- // It's OK for the type in proto layout to overlap with typeWithoutProperty in the local layout, because
- // we only use typeWithoutProperty on field stores, which can never have a proto layout.
- Type* type;
- DynamicObject* prototypeObject;
- } proto;
- struct s_accessor
- {
- DynamicObject *object;
- union
- {
- struct {
- uint16 isAccessor : 1;
- uint16 flags : 2;
- uint16 isOnProto : 1;
- uint16 unused : 12;
- };
- uint16 rawUInt16;
- };
- uint16 slotIndex;
- Type * type;
- } accessor;
- CompileAssert(sizeof(s_local) == sizeof(s_proto));
- CompileAssert(sizeof(s_local) == sizeof(s_accessor));
- } u;
- InlineCache** invalidationListSlotPtr;
- bool IsEmpty() const
- {
- return u.local.type == nullptr;
- }
- bool IsLocal() const
- {
- return u.local.isLocal;
- }
- bool IsProto() const
- {
- return u.proto.isProto;
- }
- DynamicObject * GetPrototypeObject() const
- {
- Assert(IsProto());
- return u.proto.prototypeObject;
- }
- DynamicObject * GetAccessorObject() const
- {
- Assert(IsAccessor());
- return u.accessor.object;
- }
- bool IsAccessor() const
- {
- return u.accessor.isAccessor;
- }
- bool IsAccessorOnProto() const
- {
- return IsAccessor() && u.accessor.isOnProto;
- }
- bool IsGetterAccessor() const
- {
- return IsAccessor() && !!(u.accessor.flags & InlineCacheGetterFlag);
- }
- bool IsGetterAccessorOnProto() const
- {
- return IsGetterAccessor() && u.accessor.isOnProto;
- }
- bool IsSetterAccessor() const
- {
- return IsAccessor() && !!(u.accessor.flags & InlineCacheSetterFlag);
- }
- bool IsSetterAccessorOnProto() const
- {
- return IsSetterAccessor() && u.accessor.isOnProto;
- }
- Type* GetRawType() const
- {
- return IsLocal() ? u.local.type : (IsProto() ? u.proto.type : (IsAccessor() ? u.accessor.type : nullptr));
- }
- Type* GetType() const
- {
- return TypeWithoutAuxSlotTag(GetRawType());
- }
- template<bool isAccessor>
- bool HasDifferentType(const bool isProto, const Type * type, const Type * typeWithoutProperty) const;
- bool HasType_Flags(const Type * type) const
- {
- return u.accessor.type == type || u.accessor.type == TypeWithAuxSlotTag(type);
- }
- bool HasDifferentType(const Type * type) const
- {
- return !IsEmpty() && GetType() != type;
- }
- bool RemoveFromInvalidationList()
- {
- if (this->invalidationListSlotPtr == nullptr)
- {
- return false;
- }
- *this->invalidationListSlotPtr = nullptr;
- this->invalidationListSlotPtr = nullptr;
- return true;
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- const char16 *LayoutString() const
- {
- if (IsEmpty())
- {
- return _u("Empty");
- }
- if (IsLocal())
- {
- return _u("Local");
- }
- if (IsAccessor())
- {
- return _u("Accessor");
- }
- return _u("Proto");
- }
- #endif
- public:
- void CacheLocal(
- Type *const type,
- const PropertyId propertyId,
- const PropertyIndex propertyIndex,
- const bool isInlineSlot,
- Type *const typeWithoutProperty,
- int requiredAuxSlotCapacity,
- ScriptContext *const requestContext);
- void CacheProto(
- DynamicObject *const prototypeObjectWithProperty,
- const PropertyId propertyId,
- const PropertyIndex propertyIndex,
- const bool isInlineSlot,
- const bool isMissing,
- Type *const type,
- ScriptContext *const requestContext);
- void CacheMissing(
- DynamicObject *const missingPropertyHolder,
- const PropertyId propertyId,
- const PropertyIndex propertyIndex,
- const bool isInlineSlot,
- Type *const type,
- ScriptContext *const requestContext);
- void CacheAccessor(
- const bool isGetter,
- const PropertyId propertyId,
- const PropertyIndex propertyIndex,
- const bool isInlineSlot,
- Type *const type,
- DynamicObject *const object,
- const bool isOnProto,
- ScriptContext *const requestContext);
- template<
- bool CheckLocal,
- bool CheckProto,
- bool CheckAccessor,
- bool CheckMissing,
- bool ReturnOperationInfo>
- bool TryGetProperty(
- Var const instance,
- RecyclableObject *const propertyObject,
- const PropertyId propertyId,
- Var *const propertyValue,
- ScriptContext *const requestContext,
- PropertyCacheOperationInfo *const operationInfo);
- template<
- bool CheckLocal,
- bool CheckLocalTypeWithoutProperty,
- bool CheckAccessor,
- bool ReturnOperationInfo>
- bool TrySetProperty(
- RecyclableObject *const object,
- const PropertyId propertyId,
- Var propertyValue,
- ScriptContext *const requestContext,
- PropertyCacheOperationInfo *const operationInfo,
- const PropertyOperationFlags propertyOperationFlags = PropertyOperation_None);
- bool PretendTryGetProperty(Type *const type, PropertyCacheOperationInfo * operationInfo);
- bool PretendTrySetProperty(Type *const type, Type *const oldType, PropertyCacheOperationInfo * operationInfo);
- void Clear();
- template <class TAllocator>
- InlineCache *Clone(TAllocator *const allocator);
- InlineCache *Clone(Js::PropertyId propertyId, ScriptContext* scriptContext);
- void CopyTo(PropertyId propertyId, ScriptContext * scriptContext, InlineCache * const clone);
- bool TryGetFixedMethodFromCache(Js::FunctionBody* functionBody, uint cacheId, Js::JavascriptFunction** pFixedMethod);
- bool GetGetterSetter(Type *const type, RecyclableObject **callee);
- bool GetCallApplyTarget(RecyclableObject* obj, RecyclableObject **callee);
- static uint GetGetterFlagMask()
- {
- // First bit is marked for isAccessor in the accessor cache layout.
- return InlineCacheGetterFlag << 1;
- }
- static uint GetSetterFlagMask()
- {
- // First bit is marked for isAccessor in the accessor cache layout.
- return InlineCacheSetterFlag << 1;
- }
- static uint GetGetterSetterFlagMask()
- {
- // First bit is marked for isAccessor in the accessor cache layout.
- return (InlineCacheGetterFlag | InlineCacheSetterFlag) << 1;
- }
- bool NeedsToBeRegisteredForProtoInvalidation() const;
- bool NeedsToBeRegisteredForStoreFieldInvalidation() const;
- #if DEBUG
- bool ConfirmCacheMiss(const Type * oldType, const PropertyValueInfo* info) const;
- bool NeedsToBeRegisteredForInvalidation() const;
- static void VerifyRegistrationForInvalidation(const InlineCache* cache, ScriptContext* scriptContext, Js::PropertyId propertyId);
- #endif
- #if DBG_DUMP
- void Dump();
- #endif
- };
- #if defined(_M_IX86_OR_ARM32)
- CompileAssert(sizeof(InlineCache) == 0x10);
- #else
- CompileAssert(sizeof(InlineCache) == 0x20);
- #endif
- CompileAssert(sizeof(InlineCache) == sizeof(InlineCacheAllocator::CacheLayout));
- CompileAssert(offsetof(InlineCache, invalidationListSlotPtr) == offsetof(InlineCacheAllocator::CacheLayout, strongRef));
- struct JitTimePolymorphicInlineCache;
- struct PolymorphicInlineCache sealed : public FinalizableObject
- {
- #ifdef INLINE_CACHE_STATS
- friend class Js::ScriptContext;
- #endif
- public:
- static const bool IsPolymorphic = true;
- private:
- FieldNoBarrier(InlineCache *) inlineCaches;
- Field(FunctionBody *) functionBody;
- Field(uint16) size;
- Field(bool) ignoreForEquivalentObjTypeSpec;
- Field(bool) cloneForJitTimeUse;
- Field(int32) inlineCachesFillInfo;
- // DList chaining all polymorphic inline caches of a FunctionBody together.
- // Since PolymorphicInlineCache is a leaf object, these references do not keep
- // the polymorphic inline caches alive. When a PolymorphicInlineCache is finalized,
- // it removes itself from the list and deletes its inline cache array.
- Field(PolymorphicInlineCache *) next;
- Field(PolymorphicInlineCache *) prev;
- PolymorphicInlineCache(InlineCache * inlineCaches, uint16 size, FunctionBody * functionBody)
- : inlineCaches(inlineCaches), functionBody(functionBody), size(size), ignoreForEquivalentObjTypeSpec(false), cloneForJitTimeUse(true), inlineCachesFillInfo(0), next(nullptr), prev(nullptr)
- {
- Assert((size == 0 && inlineCaches == nullptr) ||
- (inlineCaches != nullptr && size >= MinPolymorphicInlineCacheSize && size <= MaxPolymorphicInlineCacheSize));
- }
- public:
- static PolymorphicInlineCache * New(uint16 size, FunctionBody * functionBody);
- static uint16 GetInitialSize() { return MinPolymorphicInlineCacheSize; }
- bool CanAllocateBigger() { return GetSize() < MaxPolymorphicInlineCacheSize; }
- static uint16 GetNextSize(uint16 currentSize)
- {
- if (currentSize == MaxPolymorphicInlineCacheSize)
- {
- return 0;
- }
- else
- {
- Assert(currentSize >= MinPolymorphicInlineCacheSize && currentSize <= (MaxPolymorphicInlineCacheSize / 2));
- return currentSize * 2;
- }
- }
- template<bool isAccessor>
- bool HasDifferentType(const bool isProto, const Type * type, const Type * typeWithoutProperty) const;
- bool HasType_Flags(const Type * type) const;
- InlineCache * GetInlineCaches() const { return inlineCaches; }
- uint16 GetSize() const { return size; }
- PolymorphicInlineCache * GetNext() { return next; }
- bool GetIgnoreForEquivalentObjTypeSpec() const { return this->ignoreForEquivalentObjTypeSpec; }
- void SetIgnoreForEquivalentObjTypeSpec(bool value) { this->ignoreForEquivalentObjTypeSpec = value; }
- bool GetCloneForJitTimeUse() const { return this->cloneForJitTimeUse; }
- void SetCloneForJitTimeUse(bool value) { this->cloneForJitTimeUse = value; }
- uint32 GetInlineCachesFillInfo() { return this->inlineCachesFillInfo; }
- void UpdateInlineCachesFillInfo(uint32 index, bool set);
- bool IsFull();
- virtual void Finalize(bool isShutdown) override;
- virtual void Dispose(bool isShutdown) override { };
- virtual void Mark(Recycler *recycler) override { AssertMsg(false, "Mark called on object that isn't TrackableObject"); }
- void CacheLocal(
- Type *const type,
- const PropertyId propertyId,
- const PropertyIndex propertyIndex,
- const bool isInlineSlot,
- Type *const typeWithoutProperty,
- int requiredAuxSlotCapacity,
- ScriptContext *const requestContext);
- void CacheProto(
- DynamicObject *const prototypeObjectWithProperty,
- const PropertyId propertyId,
- const PropertyIndex propertyIndex,
- const bool isInlineSlot,
- const bool isMissing,
- Type *const type,
- ScriptContext *const requestContext);
- void CacheAccessor(
- const bool isGetter,
- const PropertyId propertyId,
- const PropertyIndex propertyIndex,
- const bool isInlineSlot,
- Type *const type,
- DynamicObject *const object,
- const bool isOnProto,
- ScriptContext *const requestContext);
- template<
- bool CheckLocal,
- bool CheckProto,
- bool CheckAccessor,
- bool CheckMissing,
- bool IsInlineCacheAvailable,
- bool ReturnOperationInfo>
- bool TryGetProperty(
- Var const instance,
- RecyclableObject *const propertyObject,
- const PropertyId propertyId,
- Var *const propertyValue,
- ScriptContext *const requestContext,
- PropertyCacheOperationInfo *const operationInfo,
- InlineCache *const inlineCacheToPopulate);
- template<
- bool CheckLocal,
- bool CheckLocalTypeWithoutProperty,
- bool CheckAccessor,
- bool ReturnOperationInfo,
- bool PopulateInlineCache>
- bool TrySetProperty(
- RecyclableObject *const object,
- const PropertyId propertyId,
- Var propertyValue,
- ScriptContext *const requestContext,
- PropertyCacheOperationInfo *const operationInfo,
- InlineCache *const inlineCacheToPopulate,
- const PropertyOperationFlags propertyOperationFlags = PropertyOperation_None);
- bool PretendTryGetProperty(Type *const type, PropertyCacheOperationInfo * operationInfo);
- bool PretendTrySetProperty(Type *const type, Type *const oldType, PropertyCacheOperationInfo * operationInfo);
- void CopyTo(PropertyId propertyId, ScriptContext* scriptContext, PolymorphicInlineCache *const clone);
- #if DBG_DUMP
- void Dump();
- #endif
- uint GetInlineCacheIndexForType(const Type * type) const
- {
- return (((size_t)type) >> PolymorphicInlineCacheShift) & (GetSize() - 1);
- }
- private:
- uint GetNextInlineCacheIndex(uint index) const
- {
- if (++index == GetSize())
- {
- index = 0;
- }
- return index;
- }
- template<bool CheckLocal, bool CheckProto, bool CheckAccessor>
- void CloneInlineCacheToEmptySlotInCollision(Type *const type, uint index);
- #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
- template <typename TDelegate>
- bool CheckClonedInlineCache(uint inlineCacheIndex, TDelegate mapper);
- #endif
- #if INTRUSIVE_TESTTRACE_PolymorphicInlineCache
- uint GetEntryCount()
- {
- uint count = 0;
- for (uint i = 0; i < size; ++i)
- {
- if (!inlineCaches[i].IsEmpty())
- {
- count++;
- }
- }
- return count;
- }
- #endif
- };
- #if ENABLE_NATIVE_CODEGEN
- class EquivalentTypeSet
- {
- private:
- Field(bool) sortedAndDuplicatesRemoved;
- Field(uint16) count;
- Field(RecyclerJITTypeHolder *) types;
- public:
- EquivalentTypeSet(RecyclerJITTypeHolder * types, uint16 count);
- uint16 GetCount() const
- {
- return this->count;
- }
- JITTypeHolder GetFirstType() const;
- JITTypeHolder GetType(uint16 index) const;
- bool GetSortedAndDuplicatesRemoved() const
- {
- return this->sortedAndDuplicatesRemoved;
- }
- bool Contains(const JITTypeHolder type, uint16 * pIndex = nullptr);
- static bool AreIdentical(EquivalentTypeSet * left, EquivalentTypeSet * right);
- static bool IsSubsetOf(EquivalentTypeSet * left, EquivalentTypeSet * right);
- void SortAndRemoveDuplicates();
- };
- #endif
- enum class CtorCacheGuardValues : intptr_t
- {
- TagFlag = 0x01,
- Invalid = 0x00,
- Special = TagFlag
- };
- ENUM_CLASS_HELPERS(CtorCacheGuardValues, intptr_t);
- #define MaxCachedSlotCount 65535
- struct ConstructorCache
- {
- friend class JavascriptFunction;
- struct GuardStruct
- {
- Field(CtorCacheGuardValues) value;
- };
- struct ContentStruct
- {
- Field(DynamicType*) type;
- Field(ScriptContext*) scriptContext;
- // In a pinch we could eliminate this and store type pending sharing in the type field as long
- // as the guard value flags fit below the object alignment boundary. However, this wouldn't
- // keep the type alive, so it would only work if we zeroed constructor caches before GC.
- Field(DynamicType*) pendingType;
- // We cache only types whose slotCount < 64K to ensure the slotCount field doesn't look like a pointer to the recycler.
- Field(int) slotCount;
- // This layout (i.e. one-byte bit fields first, then the one-byte updateAfterCtor, and then the two byte inlineSlotCount) is
- // chosen intentionally to make sure the whole four bytes never look like a pointer and create a false reference pinning something
- // in recycler heap. The isPopulated bit is always set when the cache holds any data - even if it got invalidated.
- Field(bool) isPopulated : 1;
- Field(bool) isPolymorphic : 1;
- Field(bool) typeUpdatePending : 1;
- Field(bool) ctorHasNoExplicitReturnValue : 1;
- Field(bool) skipDefaultNewObject : 1;
- // This field indicates that the type stored in this cache is the final type after constructor.
- Field(bool) typeIsFinal : 1;
- // This field indicates that the constructor cache has been invalidated due to a constructor's prototype property change.
- // We use this flag to determine if we should mark the cache as polymorphic and not attempt subsequent optimizations.
- // The cache may also be invalidated due to a guard invalidation resulting from some property change (e.g. in proto chain),
- // in which case we won't deem the cache polymorphic.
- Field(bool) hasPrototypeChanged : 1;
- Field(uint8) callCount;
- // Separate from the bit field below for convenient compare from the JIT-ed code. Doesn't currently increase the size.
- // If size becomes an issue, we could merge back into the bit field and use a TEST instead of CMP.
- Field(bool) updateAfterCtor;
- Field(int16) inlineSlotCount;
- };
- union
- {
- Field(GuardStruct) guard;
- Field(ContentStruct) content;
- };
- CompileAssert(offsetof(GuardStruct, value) == offsetof(ContentStruct, type));
- CompileAssert(sizeof(((GuardStruct*)nullptr)->value) == sizeof(((ContentStruct*)nullptr)->type));
- CompileAssert(static_cast<intptr_t>(CtorCacheGuardValues::Invalid) == static_cast<intptr_t>(NULL));
- static ConstructorCache DefaultInstance;
- public:
- ConstructorCache()
- {
- this->content.type = nullptr;
- 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 const * other)
- {
- Assert(other != nullptr);
- this->content.type = other->content.type;
- 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());
- }
- static size_t const GetOffsetOfGuardValue() { return offsetof(Js::ConstructorCache, guard.value); }
- static size_t const GetSizeOfGuardValue() { return sizeof(((Js::ConstructorCache*)nullptr)->guard.value); }
- void 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->content.type = 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 PopulateForSkipDefaultNewObject(ScriptContext* scriptContext)
- {
- Assert(IsConsistent());
- Assert(!this->content.isPopulated);
- this->content.isPopulated = true;
- this->guard.value = CtorCacheGuardValues::Special;
- this->content.scriptContext = scriptContext;
- this->content.skipDefaultNewObject = true;
- Assert(IsConsistent());
- }
- bool 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->content.type = type;
- this->content.typeIsFinal = true;
- this->content.pendingType = nullptr;
- }
- else
- {
- AssertMsg(false, "No one calls this part of the code?");
- this->guard.value = 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 UpdateInlineSlotCount()
- {
- Assert(IsConsistent());
- Assert(this->content.isPopulated);
- Assert(IsEnabled() || NeedsTypeUpdate());
- DynamicType* type = this->content.typeUpdatePending ? this->content.pendingType : this->content.type;
- 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 EnableAfterTypeUpdate()
- {
- Assert(IsConsistent());
- Assert(this->content.isPopulated);
- Assert(!IsEnabled());
- Assert(this->guard.value == 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());
- this->content.type = this->content.pendingType;
- this->content.typeIsFinal = true;
- this->content.pendingType = nullptr;
- this->content.typeUpdatePending = false;
- Assert(IsConsistent());
- }
- intptr_t GetRawGuardValue() const
- {
- return static_cast<intptr_t>(this->guard.value);
- }
- DynamicType* GetGuardValueAsType() const
- {
- return reinterpret_cast<DynamicType*>(this->guard.value & ~CtorCacheGuardValues::TagFlag);
- }
- DynamicType* GetType() const
- {
- Assert(static_cast<intptr_t>(this->guard.value & CtorCacheGuardValues::TagFlag) == 0);
- return this->content.type;
- }
- DynamicType* GetPendingType() const
- {
- return this->content.pendingType;
- }
- ScriptContext* GetScriptContext() const
- {
- return this->content.scriptContext;
- }
- int GetSlotCount() const
- {
- return this->content.slotCount;
- }
- int16 GetInlineSlotCount() const
- {
- return this->content.inlineSlotCount;
- }
- static bool IsDefault(const ConstructorCache* constructorCache)
- {
- return constructorCache == &ConstructorCache::DefaultInstance;
- }
- bool IsDefault() const
- {
- return IsDefault(this);
- }
- bool IsPopulated() const
- {
- Assert(IsConsistent());
- return this->content.isPopulated;
- }
- bool IsEmpty() const
- {
- Assert(IsConsistent());
- return !this->content.isPopulated;
- }
- bool IsPolymorphic() const
- {
- Assert(IsConsistent());
- return this->content.isPolymorphic;
- }
- bool GetSkipDefaultNewObject() const
- {
- return this->content.skipDefaultNewObject;
- }
- bool GetCtorHasNoExplicitReturnValue() const
- {
- return this->content.ctorHasNoExplicitReturnValue;
- }
- bool GetUpdateCacheAfterCtor() const
- {
- return this->content.updateAfterCtor;
- }
- bool GetTypeUpdatePending() const
- {
- return this->content.typeUpdatePending;
- }
- bool IsEnabled() const
- {
- return GetGuardValueAsType() != nullptr;
- }
- bool IsInvalidated() const
- {
- return this->guard.value == CtorCacheGuardValues::Invalid && this->content.isPopulated;
- }
- bool NeedsTypeUpdate() const
- {
- return this->guard.value == CtorCacheGuardValues::Special && this->content.typeUpdatePending;
- }
- uint8 CallCount() const
- {
- return content.callCount;
- }
- void IncCallCount()
- {
- ++content.callCount;
- Assert(content.callCount != 0);
- }
- bool NeedsUpdateAfterCtor() const
- {
- return this->content.updateAfterCtor;
- }
- bool IsNormal() const
- {
- return this->guard.value != CtorCacheGuardValues::Invalid && static_cast<intptr_t>(this->guard.value & CtorCacheGuardValues::TagFlag) == 0;
- }
- bool SkipDefaultNewObject() const
- {
- return this->guard.value == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject;
- }
- bool IsSetUpForJit() const
- {
- return GetRawGuardValue() != NULL && !IsPolymorphic() && !NeedsUpdateAfterCtor() && (IsNormal() || SkipDefaultNewObject());
- }
- void ClearUpdateAfterCtor()
- {
- Assert(IsConsistent());
- Assert(this->content.isPopulated);
- Assert(this->content.updateAfterCtor);
- this->content.updateAfterCtor = false;
- Assert(IsConsistent());
- }
- static ConstructorCache* EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext);
- const void* GetAddressOfGuardValue() const
- {
- return reinterpret_cast<const void*>(&this->guard.value);
- }
- static uint32 GetOffsetOfUpdateAfterCtor()
- {
- return offsetof(ConstructorCache, content.updateAfterCtor);
- }
- void InvalidateAsGuard()
- {
- Assert(!IsDefault(this));
- this->guard.value = CtorCacheGuardValues::Invalid;
- // Make sure we don't leak the types.
- Assert(this->content.type == nullptr);
- Assert(this->content.pendingType == nullptr);
- Assert(IsInvalidated());
- Assert(IsConsistent());
- }
- #if DBG
- bool IsConsistent() const
- {
- return this->guard.value == CtorCacheGuardValues::Invalid ||
- (this->content.isPopulated && (
- (this->guard.value == CtorCacheGuardValues::Special && !this->content.updateAfterCtor && this->content.skipDefaultNewObject && !this->content.typeUpdatePending && this->content.slotCount == 0 && this->content.inlineSlotCount == 0 && this->content.pendingType == nullptr) ||
- (this->guard.value == CtorCacheGuardValues::Special && !this->content.updateAfterCtor && this->content.typeUpdatePending && !this->content.skipDefaultNewObject && this->content.pendingType != nullptr) ||
- ((this->guard.value & CtorCacheGuardValues::TagFlag) == CtorCacheGuardValues::Invalid && !this->content.skipDefaultNewObject && !this->content.typeUpdatePending && this->content.pendingType == nullptr)));
- }
- #endif
- #if DBG_DUMP
- void Dump() const;
- #endif
- private:
- void InvalidateOnPrototypeChange();
- };
- // Caches the result of an instanceof operator over a type and a function
- struct IsInstInlineCache
- {
- Type * type; // The type of object operand an inline cache caches a result for
- JavascriptFunction * function; // The function operand an inline cache caches a result for
- JavascriptBoolean * result; // The result of doing (object instanceof function) where object->type == this->type
- IsInstInlineCache * next; // Used to link together caches that have the same function operand
- public:
- bool IsEmpty() const { return type == nullptr; }
- bool TryGetResult(Var instance, JavascriptFunction * function, JavascriptBoolean ** result);
- void Cache(Type * instanceType, JavascriptFunction * function, JavascriptBoolean * result, ScriptContext * scriptContext);
- void Unregister(ScriptContext * scriptContext);
- static uint32 OffsetOfFunction();
- static uint32 OffsetOfResult();
- static uint32 OffsetOfType();
- private:
- void Set(Type * instanceType, JavascriptFunction * function, JavascriptBoolean * result);
- void Clear();
- };
- // Two-entry Type-indexed circular cache
- // cache IsConcatSpreadable() result unless user-defined [@@isConcatSpreadable] exists
- class IsConcatSpreadableCache
- {
- Type *type0, *type1;
- int lastAccess;
- BOOL result0, result1;
- public:
- IsConcatSpreadableCache() :
- type0(nullptr),
- type1(nullptr),
- result0(FALSE),
- result1(FALSE),
- lastAccess(1)
- {
- }
- bool TryGetIsConcatSpreadable(Type *type, _Out_ BOOL *result)
- {
- Assert(type != nullptr);
- Assert(result != nullptr);
- *result = FALSE;
- if (type0 == type)
- {
- *result = result0;
- lastAccess = 0;
- return true;
- }
- if (type1 == type)
- {
- *result = result1;
- lastAccess = 1;
- return true;
- }
- return false;
- }
- void CacheIsConcatSpreadable(Type *type, BOOL result)
- {
- Assert(type != nullptr);
- if (lastAccess == 0)
- {
- type1 = type;
- result1 = result;
- lastAccess = 1;
- }
- else
- {
- type0 = type;
- result0 = result;
- lastAccess = 0;
- }
- }
- void Invalidate()
- {
- type0 = nullptr;
- type1 = nullptr;
- }
- };
- #if defined(_M_IX86_OR_ARM32)
- CompileAssert(sizeof(IsInstInlineCache) == 0x10);
- #else
- CompileAssert(sizeof(IsInstInlineCache) == 0x20);
- #endif
- }
|