bool DynamicObjectPropertyEnumerator::CanUseJITFastPath() const
    {
#if ENABLE_TTD
        TTDAssert(this->cachedData == nullptr || !this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode(), "We should always have cachedData null if we are in record or replay mode");
#endif

        return !this->IsNullEnumerator() && !GetEnumNonEnumerable() && this->cachedData != nullptr;
    }
    void DynamicObjectPropertyEnumerator::Reset()
    {
        if (this->object)
        {
            enumeratedCount = 0;
            initialType = object->GetDynamicType();
            objectIndex = Constants::NoBigSlot;
            initialPropertyCount = GetSnapShotSemantics() ? this->object->GetPropertyCount() : Constants::NoBigSlot;
            // Create the appropriate enumerator object.
            if (GetSnapShotSemantics() && this->initialType->PrepareForTypeSnapshotEnumeration())
            {
                ScriptContext* scriptContext = this->object->GetScriptContext();
                ThreadContext * threadContext = scriptContext->GetThreadContext();
                CachedData * data = (CachedData *)threadContext->GetDynamicObjectEnumeratorCache(this->initialType);

                if (data == nullptr || data->scriptContext != this->requestContext || data->enumNonEnumerable != GetEnumNonEnumerable() || data->enumSymbols != GetEnumSymbols())
                {
                    data = RecyclerNewStructPlus(scriptContext->GetRecycler(),
                        this->initialPropertyCount * sizeof(PropertyString *) + this->initialPropertyCount * sizeof(BigPropertyIndex) + this->initialPropertyCount * sizeof(PropertyAttributes), CachedData);
                    data->scriptContext = requestContext;
                    data->cachedCount = 0;
                    data->strings = (PropertyString **)(data + 1);
                    data->indexes = (BigPropertyIndex *)(data->strings + this->initialPropertyCount);
                    data->attributes = (PropertyAttributes*)(data->indexes + this->initialPropertyCount);
                    data->completed = false;
                    data->enumNonEnumerable = GetEnumNonEnumerable();
                    data->enumSymbols = GetEnumSymbols();
                    threadContext->AddDynamicObjectEnumeratorCache(this->initialType, data);
                }
                this->cachedData = data;
                this->cachedDataType = this->initialType;
            }
            else
            {
                this->cachedData = nullptr;
                this->cachedDataType = nullptr;
            }
        }
    }
    bool DynamicObjectPropertyEnumerator::Initialize(DynamicObject * object, EnumeratorFlags flags, ScriptContext * requestContext, ForInCache * forInCache)
    {
        this->scriptContext = requestContext;
        this->object = object;
        this->flags = flags;

        if (!object)
        {
            this->cachedData = nullptr;
            return true;
        }

        this->objectIndex = Constants::NoBigSlot;
        this->enumeratedCount = 0;

        if (!GetUseCache())
        {
            if (!object->GetDynamicType()->GetTypeHandler()->EnsureObjectReady(object))
            {
                return false;
            }
            Initialize(object->GetDynamicType(), nullptr, GetSnapShotSemantics() ? this->object->GetPropertyCount() : Constants::NoBigSlot);
            return true;
        }

        DynamicType * type = object->GetDynamicType();

        CachedData * data;
        if (forInCache && type == forInCache->type)
        {
            // We shouldn't have a for in cache when asking to enum symbols
            Assert(!GetEnumSymbols());
            data = (CachedData *)forInCache->data;

            Assert(data != nullptr);
            Assert(data->scriptContext == this->scriptContext); // The cache data script context should be the same as request context
            Assert(!data->enumSymbols);

            if (data->enumNonEnumerable == GetEnumNonEnumerable())
            {
                Initialize(type, data, data->propertyCount);
                return true;
            }
        }

        data = (CachedData *)requestContext->GetThreadContext()->GetDynamicObjectEnumeratorCache(type);

        if (data != nullptr && data->scriptContext == this->scriptContext && data->enumNonEnumerable == GetEnumNonEnumerable() && data->enumSymbols == GetEnumSymbols())
        {
            Initialize(type, data, data->propertyCount);

            if (forInCache)
            {
                forInCache->type = type;
                forInCache->data = data;
            }
            return true;
        }

        if (!object->GetDynamicType()->GetTypeHandler()->EnsureObjectReady(object))
        {
            return false;
        }

        // Reload the type after EnsureObjecteReady
        type = object->GetDynamicType();
        if (!type->PrepareForTypeSnapshotEnumeration())
        {
            Initialize(type, nullptr, object->GetPropertyCount());
            return true;
        }

        uint propertyCount = this->object->GetPropertyCount();
        data = RecyclerNewStructPlus(requestContext->GetRecycler(),
            propertyCount * sizeof(Field(PropertyString*)) + propertyCount * sizeof(BigPropertyIndex) + propertyCount * sizeof(PropertyAttributes), CachedData);
        data->scriptContext = requestContext;
        data->cachedCount = 0;
        data->propertyCount = propertyCount;
        data->strings = reinterpret_cast<Field(PropertyString*)*>(data + 1);
        data->indexes = (BigPropertyIndex *)(data->strings + propertyCount);
        data->attributes = (PropertyAttributes*)(data->indexes + propertyCount);
        data->completed = false;
        data->enumNonEnumerable = GetEnumNonEnumerable();
        data->enumSymbols = GetEnumSymbols();
        requestContext->GetThreadContext()->AddDynamicObjectEnumeratorCache(type, data);
        Initialize(type, data, propertyCount);

        if (forInCache)
        {
            forInCache->type = type;
            forInCache->data = data;
        }
        return true;
    }