//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeDebugPch.h"
#include "Library/JavascriptGenerator.h"
#include "Language/InterpreterStackFrame.h"
#include "Library/JavascriptPromise.h"

#if ENABLE_TTD

namespace TTD
{
    namespace NSSnapObjects
    {
        void ExtractCompoundObject(NSSnapObjects::SnapObject* sobj, Js::RecyclableObject* obj, bool isWellKnown, const TTDIdentifierDictionary<TTD_PTR_ID, NSSnapType::SnapType*>& idToTypeMap, SlabAllocator& alloc)
        {
            TTDAssert(!obj->IsExternal(), "We are not prepared for custom external objects yet");

            sobj->ObjectPtrId = TTD_CONVERT_VAR_TO_PTR_ID(obj);
            sobj->SnapObjectTag = obj->GetSnapTag_TTD();

            TTD_WELLKNOWN_TOKEN lookupToken = isWellKnown ? obj->GetScriptContext()->TTDWellKnownInfo->ResolvePathForKnownObject(obj) : TTD_INVALID_WELLKNOWN_TOKEN;
            sobj->OptWellKnownToken = alloc.CopyRawNullTerminatedStringInto(lookupToken);

            Js::Type* objType = obj->GetType();
            sobj->SnapType = idToTypeMap.LookupKnownItem(TTD_CONVERT_TYPEINFO_TO_PTR_ID(objType));

#if ENABLE_OBJECT_SOURCE_TRACKING
            InitializeDiagnosticOriginInformation(sobj->DiagOriginInfo);
#endif

            if(Js::StaticType::Is(objType->GetTypeId()))
            {
                NSSnapObjects::StdPropertyExtract_StaticType(sobj, obj);
            }
            else
            {
                NSSnapObjects::StdPropertyExtract_DynamicType(sobj, static_cast<Js::DynamicObject*>(obj), alloc);
            }

            obj->ExtractSnapObjectDataInto(sobj, alloc);
        }

        void StdPropertyExtract_StaticType(SnapObject* snpObject, Js::RecyclableObject* obj)
        {
            snpObject->IsCrossSite = FALSE;

            snpObject->VarArrayCount = 0;
            snpObject->VarArray = nullptr;

            snpObject->OptIndexedObjectArray = TTD_INVALID_PTR_ID;

            snpObject->OptDependsOnInfo = nullptr;
            //AddtlSnapObjectInfo must be set later in type specific extract code
        }

        void StdPropertyExtract_DynamicType(SnapObject* snpObject, Js::DynamicObject* dynObj, SlabAllocator& alloc)
        {
            NSSnapType::SnapType* sType = snpObject->SnapType;

            snpObject->IsCrossSite = dynObj->IsCrossSiteObject();

#if ENABLE_OBJECT_SOURCE_TRACKING
            CopyDiagnosticOriginInformation(snpObject->DiagOriginInfo, dynObj->TTDDiagOriginInfo);
#endif

            if(sType->TypeHandlerInfo->MaxPropertyIndex == 0)
            {
                snpObject->VarArrayCount = 0;
                snpObject->VarArray = nullptr;
            }
            else
            {
                NSSnapType::SnapHandler* sHandler = sType->TypeHandlerInfo;

                static_assert(sizeof(TTDVar) == sizeof(Js::Var), "These need to be the same size (and have same bit layout) for this to work!");

                snpObject->VarArrayCount = sHandler->MaxPropertyIndex;
                snpObject->VarArray = alloc.SlabAllocateArray<TTDVar>(snpObject->VarArrayCount);

                TTDVar* cpyBase = snpObject->VarArray;
                if(sHandler->InlineSlotCapacity != 0)
                {
                    Field(Js::Var) const* inlineSlots = dynObj->GetInlineSlots_TTD();

                    //copy all the properties (if they all fit into the inline slots) otherwise just copy all the inline slot values
                    uint32 inlineSlotCount = min(sHandler->MaxPropertyIndex, sHandler->InlineSlotCapacity);
                    js_memcpy_s(cpyBase, inlineSlotCount * sizeof(TTDVar), inlineSlots, inlineSlotCount * sizeof(Js::Var));
                }

                if(sHandler->MaxPropertyIndex > sHandler->InlineSlotCapacity)
                {
                    cpyBase = cpyBase + sHandler->InlineSlotCapacity;
                    Js::Var const* auxSlots = dynObj->GetAuxSlots_TTD();

                    //there are some values in aux slots (in addition to the inline slots) so copy them as well
                    uint32 auxSlotCount = (sHandler->MaxPropertyIndex - sHandler->InlineSlotCapacity);
                    js_memcpy_s(cpyBase, auxSlotCount * sizeof(TTDVar), auxSlots, auxSlotCount * sizeof(Js::Var));
                }
            }

            Js::ArrayObject* parray = dynObj->GetObjectArray();
            snpObject->OptIndexedObjectArray = (parray == nullptr) ? TTD_INVALID_PTR_ID : TTD_CONVERT_VAR_TO_PTR_ID(parray);

            snpObject->OptDependsOnInfo = nullptr;
            //AddtlSnapObjectInfo must be set later in type specific extract code
        }

        Js::DynamicObject* ReuseObjectCheckAndReset(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::RecyclableObject* robj = inflator->FindReusableObjectIfExists(snpObject->ObjectPtrId);
            if(robj == nullptr || Js::DynamicObject::FromVar(robj)->GetTypeId() != snpObject->SnapType->JsTypeId || Js::DynamicObject::FromVar(robj)->IsCrossSiteObject() != snpObject->IsCrossSite)
            {
                return nullptr;
            }
            TTDAssert(Js::DynamicType::Is(robj->GetTypeId()), "You should only do this for dynamic objects!!!");

            Js::DynamicObject* dynObj = Js::DynamicObject::FromVar(robj);
            return ObjectPropertyReset_General(snpObject, dynObj, inflator);
        }

        bool DoesObjectBlockScriptContextReuse(const SnapObject* snpObject, Js::DynamicObject* dynObj, InflateMap* inflator)
        {
            TTDAssert(snpObject->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN, "Only well known objects can block re-use so check that before calling this.");

            JsUtil::BaseHashSet<Js::PropertyId, HeapAllocator>& propertyReset = inflator->GetPropertyResetSet();
            propertyReset.Clear();

            ////
            for(int32 i = 0; i < dynObj->GetPropertyCount(); i++)
            {
                Js::PropertyId pid = dynObj->GetPropertyId((Js::PropertyIndex)i);
                if(pid != Js::Constants::NoProperty)
                {
                    propertyReset.AddNew(pid);
                }
            }

            const NSSnapType::SnapHandler* handler = snpObject->SnapType->TypeHandlerInfo;
            for(uint32 i = 0; i < handler->MaxPropertyIndex; ++i)
            {
                BOOL willOverwriteLater = (handler->PropertyInfoArray[i].DataKind != NSSnapType::SnapEntryDataKindTag::Clear);
                BOOL isInternal = Js::IsInternalPropertyId(handler->PropertyInfoArray[i].PropertyRecordId);

                if(willOverwriteLater | isInternal)
                {
                    Js::PropertyId pid = handler->PropertyInfoArray[i].PropertyRecordId;
                    propertyReset.Remove(pid);
                }
            }

            if(propertyReset.Count() != 0)
            {
                for(auto iter = propertyReset.GetIterator(); iter.IsValid(); iter.MoveNext())
                {
                    Js::PropertyId pid = iter.CurrentValue();
                    TTDAssert(pid != Js::Constants::NoProperty, "This shouldn't happen!!!");

                    //We don't like trying to reset these
                    if(Js::IsInternalPropertyId(pid))
                    {
                        propertyReset.Clear();
                        return true;
                    }

                    //someone added a property that is not simple to remove so let's just be safe an recreate contexts
                    if(!dynObj->IsConfigurable(pid))
                    {
                        propertyReset.Clear();
                        return true;
                    }
                }
            }

            propertyReset.Clear();
            ////

            return false;
        }

        Js::DynamicObject* ObjectPropertyReset_WellKnown(const SnapObject* snpObject, Js::DynamicObject* dynObj, InflateMap* inflator)
        {
            TTDAssert(snpObject->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN, "Should only call this on well known objects.");

            JsUtil::BaseHashSet<Js::PropertyId, HeapAllocator>& propertyReset = inflator->GetPropertyResetSet();
            propertyReset.Clear();

            ////
            for(int32 i = 0; i < dynObj->GetPropertyCount(); i++)
            {
                Js::PropertyId pid = dynObj->GetPropertyId((Js::PropertyIndex)i);
                if(pid != Js::Constants::NoProperty)
                {
                    propertyReset.AddNew(pid);
                }
            }

            const NSSnapType::SnapHandler* handler = snpObject->SnapType->TypeHandlerInfo;
            for(uint32 i = 0; i < handler->MaxPropertyIndex; ++i)
            {
                BOOL willOverwriteLater = (handler->PropertyInfoArray[i].DataKind != NSSnapType::SnapEntryDataKindTag::Clear);
                BOOL isInternal = Js::IsInternalPropertyId(handler->PropertyInfoArray[i].PropertyRecordId);

                if(willOverwriteLater | isInternal)
                {
                    Js::PropertyId pid = handler->PropertyInfoArray[i].PropertyRecordId;
                    propertyReset.Remove(pid);
                }
            }

            Js::Var undefined = dynObj->GetScriptContext()->GetLibrary()->GetUndefined();
            if(propertyReset.Count() != 0)
            {
                for(auto iter = propertyReset.GetIterator(); iter.IsValid(); iter.MoveNext())
                {
                    BOOL ok = FALSE;
                    Js::PropertyId pid = iter.CurrentValue();
                    TTDAssert(pid != Js::Constants::NoProperty && !Js::IsInternalPropertyId(pid), "This shouldn't happen!!!");

                    if(!dynObj->IsConfigurable(pid))
                    {
                        ok = dynObj->SetProperty(pid, undefined, Js::PropertyOperationFlags::PropertyOperation_Force, nullptr);
                    }
                    else
                    {
                        ok = dynObj->DeleteProperty(pid, Js::PropertyOperationFlags::PropertyOperation_Force);
                    }

                    TTDAssert(ok, "This property is stuck!!!");
                }
            }

            propertyReset.Clear();
            ////

            //always reset the index array as this is unusual and annoying to iterate over a bunch
            Js::ArrayObject* parray = dynObj->GetObjectArray();
            if(parray != nullptr)
            {
                Js::JavascriptArray* newArray = dynObj->GetLibrary()->CreateArray();
                dynObj->SetObjectArray(newArray);
            }

            return dynObj;
        }

        Js::DynamicObject* ObjectPropertyReset_General(const SnapObject* snpObject, Js::DynamicObject* dynObj, InflateMap* inflator)
        {
            TTDAssert(snpObject->OptWellKnownToken == TTD_INVALID_WELLKNOWN_TOKEN, "Should only call this on generic objects that we can fall back to re-allocating if we want.");

            if(!dynObj->GetDynamicType()->GetTypeHandler()->IsResetableForTTD(snpObject->SnapType->TypeHandlerInfo->MaxPropertyIndex))
            {
                return nullptr;
            }

            //always reset the index array as this is unusual and annoying to iterate over a bunch
            Js::ArrayObject* parray = dynObj->GetObjectArray();
            if(parray != nullptr)
            {
                Js::JavascriptArray* newArray = dynObj->GetLibrary()->CreateArray();
                dynObj->SetObjectArray(newArray);
            }

            return dynObj;
        }

        //
        //TODO: I still don't love this (and the reset above) as I feel it hits too much other execution machinery and can fail in odd cases.
        //          For the current time it is ok but we may want to look into adding specialized methods for resetting/restoring.
        //
        void StdPropertyRestore(const SnapObject* snpObject, Js::DynamicObject* obj, InflateMap* inflator)
        {
            obj->GetDynamicType()->GetTypeHandler()->EnsureObjectReady(obj);

            //Many protos are set at creation, don't mess with them if they are already correct
            if(snpObject->SnapType->PrototypeVar != nullptr)
            {
                Js::RecyclableObject* protoObj = Js::RecyclableObject::FromVar(inflator->InflateTTDVar(snpObject->SnapType->PrototypeVar));
                if(obj->GetType()->GetPrototype() != protoObj)
                {
                    obj->SetPrototype(protoObj);
                }
            }

            //set all the standard properties
            const NSSnapType::SnapHandler* handler = snpObject->SnapType->TypeHandlerInfo;

#if ENABLE_OBJECT_SOURCE_TRACKING
            CopyDiagnosticOriginInformation(obj->TTDDiagOriginInfo, snpObject->DiagOriginInfo);
#endif

            //
            //We assume that placing properties back in the same order we read them out produces correct results.
            //This is not true for enumeration -- but we handle this by explicit logging
            //There may also be sensitivity in other cases -- e.g. activation objects with arguments objects that use slot index values directly.
            //    Things look good in this case but future changes may require care and/or adding special case handling.
            //
            for(uint32 i = 0; i < handler->MaxPropertyIndex; ++i)
            {
                //We have an empty (or uninteresting) slot for so there is nothing to restore
                if(handler->PropertyInfoArray[i].DataKind == NSSnapType::SnapEntryDataKindTag::Clear)
                {
                    continue;
                }

                TTDAssert(!Js::JavascriptProxy::Is(obj), "I didn't think proxies could have real properties directly on them.");

                Js::PropertyId pid = handler->PropertyInfoArray[i].PropertyRecordId;

                if(handler->PropertyInfoArray[i].DataKind == NSSnapType::SnapEntryDataKindTag::Uninitialized)
                {
                    TTDAssert(!obj->HasOwnProperty(pid), "Shouldn't have this defined, or we should have cleared it, and nothing more to do.");

                    BOOL success = obj->EnsureProperty(pid);

                    TTDAssert(success, "Failed to set property during restore!!!");
                }
                else
                {
                    TTDVar ttdVal = snpObject->VarArray[i];
                    Js::Var pVal = (ttdVal != nullptr) ? inflator->InflateTTDVar(ttdVal) : nullptr;

                    if(handler->PropertyInfoArray[i].DataKind == NSSnapType::SnapEntryDataKindTag::Data)
                    {
                        BOOL success = FALSE;
                        if(!obj->HasOwnProperty(pid))
                        {
                            //easy case just set the property
                            success = obj->SetPropertyWithAttributes(pid, pVal, PropertyDynamicTypeDefaults, nullptr);
                        }
                        else
                        {
                            //get the value to see if it is alreay ok
                            Js::Var currentValue = nullptr;
                            Js::JavascriptOperators::GetOwnProperty(obj, pid, &currentValue, obj->GetScriptContext(), nullptr);

                            if(currentValue == pVal)
                            {
                                //the right value is already there -- easy
                                success = TRUE;
                            }
                            else
                            {
                                //Ok so now we force set the property
                                success = obj->SetPropertyWithAttributes(pid, pVal, PropertyDynamicTypeDefaults, nullptr);
                            }
                        }

                        TTDAssert(success, "Failed to set property during restore!!!");
                    }
                    else
                    {
                        NSSnapType::SnapEntryDataKindTag ttag = handler->PropertyInfoArray[i].DataKind;
                        if(ttag == NSSnapType::SnapEntryDataKindTag::Getter)
                        {
                            obj->SetAccessors(pid, pVal, nullptr);
                        }
                        else if(ttag == NSSnapType::SnapEntryDataKindTag::Setter)
                        {
                            obj->SetAccessors(pid, nullptr, pVal);
                        }
                        else
                        {
                            TTDAssert(false, "Don't know how to restore this accesstag!!");
                        }
                    }
                }

                Js::PropertyAttributes pAttrib = (Js::PropertyAttributes)handler->PropertyInfoArray[i].AttributeInfo;
                if(obj->IsWritable(pid) && (pAttrib & PropertyWritable) == PropertyNone)
                {
                    obj->SetWritable(pid, FALSE);
                }

                if(obj->IsEnumerable(pid) && (pAttrib & PropertyEnumerable) == PropertyNone)
                {
                    obj->SetEnumerable(pid, FALSE);
                }

                if(obj->IsConfigurable(pid) && (pAttrib & PropertyConfigurable) == PropertyNone)
                {
                    obj->SetConfigurable(pid, FALSE);
                }
            }

            if(snpObject->OptIndexedObjectArray != TTD_INVALID_PTR_ID)
            {
                Js::Var objArray = inflator->LookupObject(snpObject->OptIndexedObjectArray);
                obj->SetObjectArray(Js::JavascriptArray::FromAnyArray(objArray));
            }

            //finally set the extensible flag
            if(handler->IsExtensibleFlag != Js::DynamicTypeHandler::IsExtensibleFlag)
            {
                //this automatically updates the type if needed
                obj->GetDynamicType()->GetTypeHandler()->PreventExtensions(obj);
            }
            else
            {
                if(!obj->GetIsExtensible())
                {
                    TTDAssert(!(obj->GetDynamicType()->GetIsShared() || obj->GetDynamicType()->GetTypeHandler()->GetIsShared()), "We are just changing the flag so if it is shared this might unexpectedly change another type!");

                    obj->GetDynamicType()->GetTypeHandler()->SetExtensible_TTD();
                }
            }

            if(snpObject->SnapType->HasNoEnumerableProperties != obj->GetDynamicType()->GetHasNoEnumerableProperties())
            {
                obj->GetDynamicType()->SetHasNoEnumerableProperties(snpObject->SnapType->HasNoEnumerableProperties);
            }
        }

        void EmitObject(const SnapObject* snpObject, FileWriter* writer, NSTokens::Separator separator, const SnapObjectVTable* vtable, ThreadContext* threadContext)
        {
            writer->WriteRecordStart(separator);
            writer->AdjustIndent(1);

            writer->WriteAddr(NSTokens::Key::objectId, snpObject->ObjectPtrId);
            writer->WriteTag<SnapObjectType>(NSTokens::Key::objectType, snpObject->SnapObjectTag, NSTokens::Separator::CommaSeparator);

            writer->WriteBool(NSTokens::Key::isWellKnownToken, snpObject->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN, NSTokens::Separator::CommaSeparator);
            if(snpObject->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN)
            {
                writer->WriteWellKnownToken(NSTokens::Key::wellKnownToken, snpObject->OptWellKnownToken, NSTokens::Separator::CommaSeparator);
            }

            writer->WriteAddr(NSTokens::Key::typeId, snpObject->SnapType->TypePtrId, NSTokens::Separator::CommaSeparator);

            writer->WriteBool(NSTokens::Key::isCrossSite, !!snpObject->IsCrossSite, NSTokens::Separator::CommaSeparator);

#if ENABLE_OBJECT_SOURCE_TRACKING
            writer->WriteKey(NSTokens::Key::originInfo, NSTokens::Separator::CommaSeparator);
            EmitDiagnosticOriginInformation(snpObject->DiagOriginInfo, writer, NSTokens::Separator::NoSeparator);
#endif

            writer->WriteBool(NSTokens::Key::isDepOn, snpObject->OptDependsOnInfo != nullptr, NSTokens::Separator::CommaSeparator);
            if(snpObject->OptDependsOnInfo != nullptr)
            {
                writer->WriteLengthValue(snpObject->OptDependsOnInfo->DepOnCount, NSTokens::Separator::CommaSeparator);
                writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
                for(uint32 i = 0; i < snpObject->OptDependsOnInfo->DepOnCount; ++i)
                {
                    writer->WriteNakedAddr(snpObject->OptDependsOnInfo->DepOnPtrArray[i], i != 0 ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator);
                }
                writer->WriteSequenceEnd();
            }

            if(Js::DynamicType::Is(snpObject->SnapType->JsTypeId))
            {
                const NSSnapType::SnapHandler* handler = snpObject->SnapType->TypeHandlerInfo;

                writer->WriteAddr(NSTokens::Key::objectId, snpObject->OptIndexedObjectArray, NSTokens::Separator::CommaSeparator);

                if(handler->MaxPropertyIndex == 0)
                {
                    writer->WriteLengthValue(snpObject->VarArrayCount, NSTokens::Separator::CommaSeparator);
                }
                else
                {
                    writer->WriteLengthValue(handler->MaxPropertyIndex, NSTokens::Separator::CommaAndBigSpaceSeparator);
                    writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
                    writer->AdjustIndent(1);
                    for(uint32 i = 0; i < handler->MaxPropertyIndex; ++i)
                    {
                        NSTokens::Separator varSep = i != 0 ? NSTokens::Separator::CommaAndBigSpaceSeparator : NSTokens::Separator::BigSpaceSeparator;

                        if(handler->PropertyInfoArray[i].DataKind == NSSnapType::SnapEntryDataKindTag::Clear)
                        {
                            writer->WriteNakedNull(varSep);
                        }
                        else
                        {
#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
                            writer->WriteRecordStart(varSep);
                            writer->WriteUInt32(NSTokens::Key::pid, (uint32)handler->PropertyInfoArray[i].PropertyRecordId, NSTokens::Separator::NoSeparator);
                            writer->WriteKey(NSTokens::Key::entry, NSTokens::Separator::CommaSeparator);

                            varSep = NSTokens::Separator::NoSeparator;
#endif

                            NSSnapValues::EmitTTDVar(snpObject->VarArray[i], writer, varSep);

#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
                            writer->WriteRecordEnd();
#endif
                        }
                    }
                    writer->AdjustIndent(-1);
                    writer->WriteSequenceEnd(NSTokens::Separator::BigSpaceSeparator);
                }
            }

            fPtr_EmitAddtlInfo addtlInfoEmit = vtable[(uint32)snpObject->SnapObjectTag].EmitAddtlInfoFunc;
            if(addtlInfoEmit != nullptr)
            {
                addtlInfoEmit(snpObject, writer);
            }

            writer->AdjustIndent(-1);
            writer->WriteRecordEnd(NSTokens::Separator::BigSpaceSeparator);
        }

        void ParseObject(SnapObject* snpObject, bool readSeparator, FileReader* reader, SlabAllocator& alloc, const SnapObjectVTable* vtable, const TTDIdentifierDictionary<TTD_PTR_ID, NSSnapType::SnapType*>& ptrIdToTypeMap)
        {
            reader->ReadRecordStart(readSeparator);

            snpObject->ObjectPtrId = reader->ReadAddr(NSTokens::Key::objectId);
            snpObject->SnapObjectTag = reader->ReadTag<SnapObjectType>(NSTokens::Key::objectType, true);

            bool hasWellKnownToken = reader->ReadBool(NSTokens::Key::isWellKnownToken, true);
            snpObject->OptWellKnownToken = TTD_INVALID_WELLKNOWN_TOKEN;
            if(hasWellKnownToken)
            {
                snpObject->OptWellKnownToken = reader->ReadWellKnownToken(NSTokens::Key::wellKnownToken, alloc, true);
            }

            snpObject->SnapType = ptrIdToTypeMap.LookupKnownItem(reader->ReadAddr(NSTokens::Key::typeId, true));

            snpObject->IsCrossSite = reader->ReadBool(NSTokens::Key::isCrossSite, true);

#if ENABLE_OBJECT_SOURCE_TRACKING
            reader->ReadKey(NSTokens::Key::originInfo, true);
            ParseDiagnosticOriginInformation(snpObject->DiagOriginInfo, false, reader);
#endif

            snpObject->OptDependsOnInfo = nullptr;
            bool isDepOn = reader->ReadBool(NSTokens::Key::isDepOn, true);
            if(isDepOn)
            {
                snpObject->OptDependsOnInfo = alloc.SlabAllocateStruct<DependsOnInfo>();

                snpObject->OptDependsOnInfo->DepOnCount = reader->ReadLengthValue(true);
                snpObject->OptDependsOnInfo->DepOnPtrArray = alloc.SlabAllocateArray<TTD_PTR_ID>(snpObject->OptDependsOnInfo->DepOnCount);

                reader->ReadSequenceStart_WDefaultKey(true);
                for(uint32 i = 0; i < snpObject->OptDependsOnInfo->DepOnCount; ++i)
                {
                    snpObject->OptDependsOnInfo->DepOnPtrArray[i] = reader->ReadNakedAddr(i != 0);
                }
                reader->ReadSequenceEnd();
            }

            if(Js::DynamicType::Is(snpObject->SnapType->JsTypeId))
            {
                const NSSnapType::SnapHandler* handler = snpObject->SnapType->TypeHandlerInfo;

                snpObject->OptIndexedObjectArray = reader->ReadAddr(NSTokens::Key::objectId, true);

                snpObject->VarArrayCount = reader->ReadLengthValue(true);
                if(handler->MaxPropertyIndex == 0)
                {
                    snpObject->VarArray = nullptr;
                }
                else
                {
                    snpObject->VarArray = alloc.SlabAllocateArray<TTDVar>(snpObject->VarArrayCount);

                    reader->ReadSequenceStart_WDefaultKey(true);
                    for(uint32 i = 0; i < handler->MaxPropertyIndex; ++i)
                    {
                        bool readVarSeparator = i != 0;

                        if(handler->PropertyInfoArray[i].DataKind == NSSnapType::SnapEntryDataKindTag::Clear)
                        {
                            reader->ReadNakedNull(readVarSeparator);
                        }
                        else
                        {
#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
                            reader->ReadRecordStart(readVarSeparator);
                            reader->ReadUInt32(NSTokens::Key::pid);
                            reader->ReadKey(NSTokens::Key::entry, true);

                            readVarSeparator = false;
#endif

                            snpObject->VarArray[i] = NSSnapValues::ParseTTDVar(readVarSeparator, reader);

#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
                            reader->ReadRecordEnd();
#endif
                        }
                    }
                    reader->ReadSequenceEnd();
                }
            }

            fPtr_ParseAddtlInfo addtlInfoParse = vtable[(uint32)snpObject->SnapObjectTag].ParseAddtlInfoFunc;
            if(addtlInfoParse != nullptr)
            {
                addtlInfoParse(snpObject, reader, alloc);
            }

            reader->ReadRecordEnd();
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            compareMap.DiagnosticAssert(sobj1->SnapObjectTag == sobj2->SnapObjectTag);
            compareMap.DiagnosticAssert(TTD_DIAGNOSTIC_COMPARE_WELLKNOWN_TOKENS(sobj1->OptWellKnownToken, sobj2->OptWellKnownToken));

            NSSnapType::AssertSnapEquiv(sobj1->SnapType, sobj2->SnapType, compareMap);

            //Depends on info is a function of the rest of the properties so we don't need to explicitly check it.
            //But for sanity assert same counts.
            compareMap.DiagnosticAssert((sobj1->OptDependsOnInfo == nullptr && sobj2->OptDependsOnInfo == nullptr) || (sobj1->OptDependsOnInfo->DepOnCount == sobj2->OptDependsOnInfo->DepOnCount));

            //we allow the replay in debug mode to be cross site even if orig was not (that is ok) but if record was x-site then replay must be as well
            if(compareMap.StrictCrossSite)
            {
                compareMap.DiagnosticAssert(sobj1->IsCrossSite == sobj2->IsCrossSite);
            }
            else
            {
                compareMap.DiagnosticAssert(!sobj1->IsCrossSite || sobj2->IsCrossSite);
            }

#if ENABLE_OBJECT_SOURCE_TRACKING
            compareMap.DiagnosticAssert(sobj1->DiagOriginInfo.SourceLine == sobj2->DiagOriginInfo.SourceLine);
            compareMap.DiagnosticAssert(sobj1->DiagOriginInfo.EventTime == sobj2->DiagOriginInfo.EventTime);
            compareMap.DiagnosticAssert(sobj1->DiagOriginInfo.TimeHash == sobj2->DiagOriginInfo.TimeHash);
#endif

            compareMap.DiagnosticAssert(Js::DynamicType::Is(sobj1->SnapType->JsTypeId) == Js::DynamicType::Is(sobj2->SnapType->JsTypeId));
            if(Js::DynamicType::Is(sobj1->SnapType->JsTypeId))
            {
                compareMap.CheckConsistentAndAddPtrIdMapping_Special(sobj1->OptIndexedObjectArray, sobj2->OptIndexedObjectArray, _u("indexedObjectArray"));

                const NSSnapType::SnapHandler* handler1 = sobj1->SnapType->TypeHandlerInfo;
                JsUtil::BaseDictionary<int64, int32, HeapAllocator> sobj1PidMap(&HeapAllocator::Instance);
                for(uint32 i = 0; i < handler1->MaxPropertyIndex; ++i)
                {
                    const NSSnapType::SnapHandlerPropertyEntry spe = handler1->PropertyInfoArray[i];
                    if(spe.DataKind != NSSnapType::SnapEntryDataKindTag::Clear)
                    {
                        int64 locationTag = ComputeLocationTagForAssertCompare(spe);
                        sobj1PidMap.AddNew(locationTag, (int32)i);
                    }
                }

                const NSSnapType::SnapHandler* handler2 = sobj2->SnapType->TypeHandlerInfo;
                for(uint32 i = 0; i < handler2->MaxPropertyIndex; ++i)
                {
                    const NSSnapType::SnapHandlerPropertyEntry spe = handler2->PropertyInfoArray[i];
                    if(spe.DataKind != NSSnapType::SnapEntryDataKindTag::Clear && spe.DataKind != NSSnapType::SnapEntryDataKindTag::Uninitialized)
                    {
                        int64 locationTag = ComputeLocationTagForAssertCompare(spe);

                        int32 idx1 = sobj1PidMap.LookupWithKey(locationTag, -1);
                        compareMap.DiagnosticAssert(idx1 != -1);

                        TTDVar var1 = sobj1->VarArray[idx1];
                        TTDVar var2 = sobj2->VarArray[i];

                        if(spe.DataKind == NSSnapType::SnapEntryDataKindTag::Data)
                        {
                            NSSnapValues::AssertSnapEquivTTDVar_Property(var1, var2, compareMap, spe.PropertyRecordId);
                        }
                        else if(spe.DataKind == NSSnapType::SnapEntryDataKindTag::Getter)
                        {
                            NSSnapValues::AssertSnapEquivTTDVar_PropertyGetter(var1, var2, compareMap, spe.PropertyRecordId);
                        }
                        else
                        {
                            TTDAssert(spe.DataKind == NSSnapType::SnapEntryDataKindTag::Setter, "What other tags are there???");

                            NSSnapValues::AssertSnapEquivTTDVar_PropertySetter(var1, var2, compareMap, spe.PropertyRecordId);
                        }
                    }
                }
            }

            fPtr_AssertSnapEquivAddtlInfo equivCheck = compareMap.SnapObjCmpVTable[(int32)sobj1->SnapObjectTag];
            if(equivCheck != nullptr)
            {
                equivCheck(sobj1, sobj2, compareMap);
            }
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapDynamicObject(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::DynamicObject* rcObj = ReuseObjectCheckAndReset(snpObject, inflator);
            if(rcObj != nullptr)
            {
                return rcObj;
            }
            else
            {
                Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
                return ctx->GetLibrary()->CreateObject();
            }
        }

        Js::RecyclableObject* DoObjectInflation_SnapExternalObject(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::DynamicObject* rcObj = ReuseObjectCheckAndReset(snpObject, inflator);
            if(rcObj != nullptr)
            {
                return rcObj;
            }
            else
            {
                Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
                Js::Var res = nullptr;
                ctx->GetThreadContext()->TTDContext->TTDExternalObjectFunctions.pfCreateExternalObject(ctx, nullptr, &res);

                return Js::RecyclableObject::FromVar(res);
            }
        }

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapScriptFunctionInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapScriptFunctionInfo* snapFuncInfo = SnapObjectGetAddtlInfoAs<SnapScriptFunctionInfo*, SnapObjectType::SnapScriptFunctionObject>(snpObject);

            Js::FunctionBody* fbody = inflator->LookupFunctionBody(snapFuncInfo->BodyRefId);

            Js::ScriptFunction* func = nullptr;
            if(!fbody->GetInlineCachesOnFunctionObject())
            {
                func = ctx->GetLibrary()->CreateScriptFunction(fbody);
            }
            else
            {
                Js::ScriptFunctionWithInlineCache* ifunc = ctx->GetLibrary()->CreateScriptFunctionWithInlineCache(fbody);
                ifunc->CreateInlineCache();

                func = ifunc;
            }

            return func;
        }

        void DoAddtlValueInstantiation_SnapScriptFunctionInfo(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        {
            Js::ScriptFunction* fobj = Js::ScriptFunction::FromVar(obj);
            SnapScriptFunctionInfo* snapFuncInfo = SnapObjectGetAddtlInfoAs<SnapScriptFunctionInfo*, SnapObjectType::SnapScriptFunctionObject>(snpObject);
            DoAddtlValueInstantiation_SnapScriptFunctionInfoEx(snapFuncInfo, fobj, inflator);
        }

        void DoAddtlValueInstantiation_SnapScriptFunctionInfoEx(const SnapScriptFunctionInfo* snapFuncInfo, Js::ScriptFunction* func, InflateMap* inflator)
        {
            if(snapFuncInfo->CachedScopeObjId != TTD_INVALID_PTR_ID)
            {
                func->SetCachedScope(Js::ActivationObjectEx::FromVar(inflator->LookupObject(snapFuncInfo->CachedScopeObjId)));
            }

            if(snapFuncInfo->HomeObjId != TTD_INVALID_PTR_ID)
            {
                func->SetHomeObj(inflator->LookupObject(snapFuncInfo->HomeObjId));
            }

            if(snapFuncInfo->ScopeId != TTD_INVALID_PTR_ID)
            {
                Js::FrameDisplay* environment = inflator->LookupEnvironment(snapFuncInfo->ScopeId);
                func->SetEnvironment(environment);
            }

            if(snapFuncInfo->ComputedNameInfo != nullptr)
            {
                Js::Var cNameVar = inflator->InflateTTDVar(snapFuncInfo->ComputedNameInfo);
                func->SetComputedNameVar(cNameVar);
            }
        }

        void EmitAddtlInfo_SnapScriptFunctionInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapScriptFunctionInfo* snapFuncInfo = SnapObjectGetAddtlInfoAs<SnapScriptFunctionInfo*, SnapObjectType::SnapScriptFunctionObject>(snpObject);
            EmitAddtlInfo_SnapScriptFunctionInfoEx(snapFuncInfo, writer);
        }

        void EmitAddtlInfo_SnapScriptFunctionInfoEx(const SnapScriptFunctionInfo* snapFuncInfo, FileWriter* writer)
        {

            writer->WriteAddr(NSTokens::Key::functionBodyId, snapFuncInfo->BodyRefId, NSTokens::Separator::CommaAndBigSpaceSeparator);

            writer->WriteString(NSTokens::Key::name, snapFuncInfo->DebugFunctionName, NSTokens::Separator::CommaSeparator);
            writer->WriteAddr(NSTokens::Key::cachedScopeObjId, snapFuncInfo->CachedScopeObjId, NSTokens::Separator::CommaSeparator);
            writer->WriteAddr(NSTokens::Key::scopeId, snapFuncInfo->ScopeId, NSTokens::Separator::CommaSeparator);
            writer->WriteAddr(NSTokens::Key::ptrIdVal, snapFuncInfo->HomeObjId, NSTokens::Separator::CommaSeparator);

            writer->WriteKey(NSTokens::Key::nameInfo, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(snapFuncInfo->ComputedNameInfo, writer, NSTokens::Separator::NoSeparator);
        }

        void ParseAddtlInfo_SnapScriptFunctionInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapScriptFunctionInfo* snapFuncInfo = alloc.SlabAllocateStruct<SnapScriptFunctionInfo>();
            ParseAddtlInfo_SnapScriptFunctionInfoEx(snapFuncInfo, reader, alloc);
            SnapObjectSetAddtlInfoAs<SnapScriptFunctionInfo*, SnapObjectType::SnapScriptFunctionObject>(snpObject, snapFuncInfo);
        }

        void ParseAddtlInfo_SnapScriptFunctionInfoEx(SnapScriptFunctionInfo* snapFuncInfo, FileReader* reader, SlabAllocator& alloc)
        {
            snapFuncInfo->BodyRefId = reader->ReadAddr(NSTokens::Key::functionBodyId, true);
            reader->ReadString(NSTokens::Key::name, alloc, snapFuncInfo->DebugFunctionName, true);
            snapFuncInfo->CachedScopeObjId = reader->ReadAddr(NSTokens::Key::cachedScopeObjId, true);
            snapFuncInfo->ScopeId = reader->ReadAddr(NSTokens::Key::scopeId, true);
            snapFuncInfo->HomeObjId = reader->ReadAddr(NSTokens::Key::ptrIdVal, true);

            reader->ReadKey(NSTokens::Key::nameInfo, true);
            snapFuncInfo->ComputedNameInfo = NSSnapValues::ParseTTDVar(false, reader);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapScriptFunctionInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const SnapScriptFunctionInfo* snapFuncInfo1 = SnapObjectGetAddtlInfoAs<SnapScriptFunctionInfo*, SnapObjectType::SnapScriptFunctionObject>(sobj1);
            const SnapScriptFunctionInfo* snapFuncInfo2 = SnapObjectGetAddtlInfoAs<SnapScriptFunctionInfo*, SnapObjectType::SnapScriptFunctionObject>(sobj2);

            compareMap.DiagnosticAssert(TTStringEQForDiagnostics(snapFuncInfo1->DebugFunctionName, snapFuncInfo2->DebugFunctionName));

            compareMap.CheckConsistentAndAddPtrIdMapping_FunctionBody(snapFuncInfo1->BodyRefId, snapFuncInfo2->BodyRefId);
            compareMap.CheckConsistentAndAddPtrIdMapping_Special(snapFuncInfo1->ScopeId, snapFuncInfo2->ScopeId, _u("scopes"));

            compareMap.CheckConsistentAndAddPtrIdMapping_Special(snapFuncInfo1->CachedScopeObjId, snapFuncInfo2->CachedScopeObjId, _u("cachedScopeObj"));
            compareMap.CheckConsistentAndAddPtrIdMapping_Special(snapFuncInfo1->HomeObjId, snapFuncInfo2->HomeObjId, _u("homeObject"));

            NSSnapValues::AssertSnapEquivTTDVar_Special(snapFuncInfo1->ComputedNameInfo, snapFuncInfo2->ComputedNameInfo, compareMap, _u("computedName"));
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapExternalFunctionInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            TTDVar snapVar = SnapObjectGetAddtlInfoAs<TTDVar, SnapObjectType::SnapExternalFunctionObject>(snpObject);

            Js::Var fname = (snapVar != nullptr) ? inflator->InflateTTDVar(snapVar) : nullptr;
            return ctx->GetLibrary()->CreateExternalFunction_TTD(fname);
        }

        void EmitAddtlInfo_SnapExternalFunctionInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            TTDVar snapName = SnapObjectGetAddtlInfoAs<TTDVar, SnapObjectType::SnapExternalFunctionObject>(snpObject);

            writer->WriteKey(NSTokens::Key::name, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(snapName, writer, NSTokens::Separator::NoSeparator);
        }

        void ParseAddtlInfo_SnapExternalFunctionInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            reader->ReadKey(NSTokens::Key::name, true);
            TTDVar snapName = NSSnapValues::ParseTTDVar(false, reader);

            SnapObjectSetAddtlInfoAs<TTDVar, SnapObjectType::SnapExternalFunctionObject>(snpObject, snapName);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapExternalFunctionInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            TTDVar snapName1 = SnapObjectGetAddtlInfoAs<TTDVar, SnapObjectType::SnapExternalFunctionObject>(sobj1);
            TTDVar snapName2 = SnapObjectGetAddtlInfoAs<TTDVar, SnapObjectType::SnapExternalFunctionObject>(sobj2);

            NSSnapValues::AssertSnapEquivTTDVar_Special(snapName1, snapName2, compareMap, _u("externalFunctionName"));
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapRevokerFunctionInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            TTD_PTR_ID* proxyId = SnapObjectGetAddtlInfoAs<TTD_PTR_ID*, SnapObjectType::SnapRuntimeRevokerFunctionObject>(snpObject);
            Js::RecyclableObject* proxyObj = nullptr;
            if(*proxyId == TTD_INVALID_PTR_ID)
            {
                proxyObj = ctx->GetLibrary()->GetNull();
            }
            else
            {
                proxyObj = inflator->LookupObject(*proxyId);
            }

            return ctx->GetLibrary()->CreateRevokeFunction_TTD(proxyObj);
        }

        void EmitAddtlInfo_SnapRevokerFunctionInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            TTD_PTR_ID* revokeTrgt = SnapObjectGetAddtlInfoAs<TTD_PTR_ID*, SnapObjectType::SnapRuntimeRevokerFunctionObject>(snpObject);

            writer->WriteAddr(NSTokens::Key::objectId, *revokeTrgt, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapRevokerFunctionInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            TTD_PTR_ID* revokerId = alloc.SlabAllocateStruct<TTD_PTR_ID>();
            *revokerId = reader->ReadAddr(NSTokens::Key::objectId, true);

            SnapObjectSetAddtlInfoAs<TTD_PTR_ID*, SnapObjectType::SnapRuntimeRevokerFunctionObject>(snpObject, revokerId);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapRevokerFunctionInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            TTD_PTR_ID* revokeTrgt1 = SnapObjectGetAddtlInfoAs<TTD_PTR_ID*, SnapObjectType::SnapRuntimeRevokerFunctionObject>(sobj1);
            TTD_PTR_ID* revokeTrgt2 = SnapObjectGetAddtlInfoAs<TTD_PTR_ID*, SnapObjectType::SnapRuntimeRevokerFunctionObject>(sobj2);

            compareMap.CheckConsistentAndAddPtrIdMapping_Special(*revokeTrgt1, *revokeTrgt2, _u("revokeTarget"));
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapBoundFunctionInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            //Bound functions are not too common and have special internal state so it seems easiest to always re-create them.
            //We can re-evaluate this choice later if needed.

            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapBoundFunctionInfo* snapBoundInfo = SnapObjectGetAddtlInfoAs<SnapBoundFunctionInfo*, SnapObjectType::SnapBoundFunctionObject>(snpObject);

            Js::RecyclableObject* bFunction = inflator->LookupObject(snapBoundInfo->TargetFunction);
            Js::RecyclableObject* bThis = (snapBoundInfo->BoundThis != TTD_INVALID_PTR_ID) ? inflator->LookupObject(snapBoundInfo->BoundThis) : nullptr;

            Field(Js::Var)* bArgs = nullptr;
            if(snapBoundInfo->ArgCount != 0)
            {
                bArgs = RecyclerNewArray(ctx->GetRecycler(), Field(Js::Var), snapBoundInfo->ArgCount);

                for(uint i = 0; i < snapBoundInfo->ArgCount; i++)
                {
                    bArgs[i] = inflator->InflateTTDVar(snapBoundInfo->ArgArray[i]);
                }
            }

            return ctx->GetLibrary()->CreateBoundFunction_TTD(bFunction, bThis, snapBoundInfo->ArgCount, bArgs);
        }

        void EmitAddtlInfo_SnapBoundFunctionInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapBoundFunctionInfo* snapBoundInfo = SnapObjectGetAddtlInfoAs<SnapBoundFunctionInfo*, SnapObjectType::SnapBoundFunctionObject>(snpObject);

            writer->WriteAddr(NSTokens::Key::boundFunction, snapBoundInfo->TargetFunction, NSTokens::Separator::CommaAndBigSpaceSeparator);
            writer->WriteAddr(NSTokens::Key::boundThis, snapBoundInfo->BoundThis, NSTokens::Separator::CommaSeparator);
            writer->WriteLengthValue(snapBoundInfo->ArgCount, NSTokens::Separator::CommaSeparator);

            writer->WriteKey(NSTokens::Key::boundArgs, NSTokens::Separator::CommaAndBigSpaceSeparator);
            writer->WriteSequenceStart();
            for(uint32 i = 0; i < snapBoundInfo->ArgCount; ++i)
            {
                NSSnapValues::EmitTTDVar(snapBoundInfo->ArgArray[i], writer, i != 0 ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator);
            }
            writer->WriteSequenceEnd();
        }

        void ParseAddtlInfo_SnapBoundFunctionInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapBoundFunctionInfo* snapBoundInfo = alloc.SlabAllocateStruct<SnapBoundFunctionInfo>();

            snapBoundInfo->TargetFunction = reader->ReadAddr(NSTokens::Key::boundFunction, true);
            snapBoundInfo->BoundThis = reader->ReadAddr(NSTokens::Key::boundThis, true);
            snapBoundInfo->ArgCount = reader->ReadLengthValue(true);

            if(snapBoundInfo->ArgCount == 0)
            {
                snapBoundInfo->ArgArray = nullptr;
            }
            else
            {
                snapBoundInfo->ArgArray = alloc.SlabAllocateArray<TTDVar>(snapBoundInfo->ArgCount);
            }

            reader->ReadKey(NSTokens::Key::boundArgs, true);
            reader->ReadSequenceStart();
            for(uint32 i = 0; i < snapBoundInfo->ArgCount; ++i)
            {
                snapBoundInfo->ArgArray[i] = NSSnapValues::ParseTTDVar(i != 0, reader);
            }
            reader->ReadSequenceEnd();

            SnapObjectSetAddtlInfoAs<SnapBoundFunctionInfo*, SnapObjectType::SnapBoundFunctionObject>(snpObject, snapBoundInfo);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapBoundFunctionInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            SnapBoundFunctionInfo* snapBoundInfo1 = SnapObjectGetAddtlInfoAs<SnapBoundFunctionInfo*, SnapObjectType::SnapBoundFunctionObject>(sobj1);
            SnapBoundFunctionInfo* snapBoundInfo2 = SnapObjectGetAddtlInfoAs<SnapBoundFunctionInfo*, SnapObjectType::SnapBoundFunctionObject>(sobj2);

            compareMap.CheckConsistentAndAddPtrIdMapping_Special(snapBoundInfo1->TargetFunction, snapBoundInfo2->TargetFunction, _u("targetFunction"));
            compareMap.CheckConsistentAndAddPtrIdMapping_Special(snapBoundInfo1->BoundThis, snapBoundInfo2->BoundThis, _u("boundThis"));

            compareMap.DiagnosticAssert(snapBoundInfo1->ArgCount == snapBoundInfo2->ArgCount);
            for(uint32 i = 0; i < snapBoundInfo1->ArgCount; ++i)
            {
                NSSnapValues::AssertSnapEquivTTDVar_SpecialArray(snapBoundInfo1->ArgArray[i], snapBoundInfo2->ArgArray[i], compareMap, _u("boundArgs"), i);
            }
        }
#endif

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapActivationInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            return ctx->GetLibrary()->CreateActivationObject();
        }

        Js::RecyclableObject* DoObjectInflation_SnapBlockActivationObject(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            return ctx->GetLibrary()->CreateBlockActivationObject();
        }

        Js::RecyclableObject* DoObjectInflation_SnapPseudoActivationObject(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            return ctx->GetLibrary()->CreatePseudoActivationObject();
        }

        Js::RecyclableObject* DoObjectInflation_SnapConsoleScopeActivationObject(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            return ctx->GetLibrary()->CreateConsoleScopeActivationObject();
        }

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapHeapArgumentsInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            SnapHeapArgumentsInfo* argsInfo = SnapObjectGetAddtlInfoAs<SnapHeapArgumentsInfo*, SnapObjectType::SnapHeapArgumentsObject>(snpObject);

            Js::RecyclableObject* activationObj = nullptr;
            if(argsInfo->IsFrameNullPtr)
            {
                activationObj = nullptr;
            }
            else
            {
                TTDAssert(argsInfo->FrameObject != TTD_INVALID_PTR_ID, "That won't work!");

                activationObj = inflator->LookupObject(argsInfo->FrameObject);
            }

            return  ctx->GetLibrary()->CreateHeapArguments_TTD(argsInfo->NumOfArguments, argsInfo->FormalCount, static_cast<Js::ActivationObject*>(activationObj), argsInfo->DeletedArgFlags);
        }

        Js::RecyclableObject* DoObjectInflation_SnapES5HeapArgumentsInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            SnapHeapArgumentsInfo* argsInfo = SnapObjectGetAddtlInfoAs<SnapHeapArgumentsInfo*, SnapObjectType::SnapES5HeapArgumentsObject>(snpObject);

            Js::RecyclableObject* activationObj = nullptr;
            if(argsInfo->IsFrameNullPtr)
            {
                activationObj = nullptr;
            }
            else
            {
                TTDAssert(argsInfo->FrameObject != TTD_INVALID_PTR_ID, "That won't work!");

                activationObj = inflator->LookupObject(argsInfo->FrameObject);
            }

            return  ctx->GetLibrary()->CreateES5HeapArguments_TTD(argsInfo->NumOfArguments, argsInfo->FormalCount, static_cast<Js::ActivationObject*>(activationObj), argsInfo->DeletedArgFlags);
        }

        //////////////////

        ////
        //Promise Info

        Js::RecyclableObject* DoObjectInflation_SnapPromiseInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            const SnapPromiseInfo* promiseInfo = SnapObjectGetAddtlInfoAs<SnapPromiseInfo*, SnapObjectType::SnapPromiseObject>(snpObject);
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            Js::Var result = (promiseInfo->Result != nullptr) ? inflator->InflateTTDVar(promiseInfo->Result) : nullptr;

            SList<Js::JavascriptPromiseReaction*, HeapAllocator> resolveReactions(&HeapAllocator::Instance);
            for(uint32 i = 0; i < promiseInfo->ResolveReactionCount; ++i)
            {
                Js::JavascriptPromiseReaction* reaction = NSSnapValues::InflatePromiseReactionInfo(promiseInfo->ResolveReactions + i, ctx, inflator);
                resolveReactions.Prepend(reaction);
            }
            resolveReactions.Reverse();

            SList<Js::JavascriptPromiseReaction*, HeapAllocator> rejectReactions(&HeapAllocator::Instance);
            for(uint32 i = 0; i < promiseInfo->RejectReactionCount; ++i)
            {
                Js::JavascriptPromiseReaction* reaction = NSSnapValues::InflatePromiseReactionInfo(promiseInfo->RejectReactions + i, ctx, inflator);
                rejectReactions.Prepend(reaction);
            }
            rejectReactions.Reverse();

            Js::RecyclableObject* res = ctx->GetLibrary()->CreatePromise_TTD(promiseInfo->Status, promiseInfo->isHandled, result, resolveReactions, rejectReactions);

            return res;
        }

        void EmitAddtlInfo_SnapPromiseInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapPromiseInfo* promiseInfo = SnapObjectGetAddtlInfoAs<SnapPromiseInfo*, SnapObjectType::SnapPromiseObject>(snpObject);

            writer->WriteUInt32(NSTokens::Key::u32Val, promiseInfo->Status, NSTokens::Separator::CommaSeparator);
            writer->WriteBool(NSTokens::Key::boolVal, promiseInfo->isHandled, NSTokens::Separator::CommaSeparator);

            writer->WriteKey(NSTokens::Key::resultValue, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(promiseInfo->Result, writer, NSTokens::Separator::NoSeparator);

            writer->WriteLengthValue(promiseInfo->ResolveReactionCount, NSTokens::Separator::CommaSeparator);
            writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
            for(uint32 i = 0; i < promiseInfo->ResolveReactionCount; ++i)
            {
                NSTokens::Separator sep = (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator;
                NSSnapValues::EmitPromiseReactionInfo(promiseInfo->ResolveReactions + i, writer, sep);
            }
            writer->WriteSequenceEnd();

            writer->WriteLengthValue(promiseInfo->RejectReactionCount, NSTokens::Separator::CommaSeparator);
            writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
            for(uint32 i = 0; i < promiseInfo->RejectReactionCount; ++i)
            {
                NSTokens::Separator sep = (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator;
                NSSnapValues::EmitPromiseReactionInfo(promiseInfo->RejectReactions + i, writer, sep);
            }
            writer->WriteSequenceEnd();
        }

        void ParseAddtlInfo_SnapPromiseInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapPromiseInfo* promiseInfo = alloc.SlabAllocateStruct<SnapPromiseInfo>();

            promiseInfo->Status = reader->ReadUInt32(NSTokens::Key::u32Val, true);
            promiseInfo->isHandled = reader->ReadBool(NSTokens::Key::boolVal, true);

            reader->ReadKey(NSTokens::Key::resultValue, true);
            promiseInfo->Result = NSSnapValues::ParseTTDVar(false, reader);

            promiseInfo->ResolveReactionCount = reader->ReadLengthValue(true);
            promiseInfo->ResolveReactions = nullptr;
            reader->ReadSequenceStart_WDefaultKey(true);
            if(promiseInfo->ResolveReactionCount != 0)
            {
                promiseInfo->ResolveReactions = alloc.SlabAllocateArray<NSSnapValues::SnapPromiseReactionInfo>(promiseInfo->ResolveReactionCount);

                for(uint32 i = 0; i < promiseInfo->ResolveReactionCount; ++i)
                {
                    NSSnapValues::ParsePromiseReactionInfo(promiseInfo->ResolveReactions + i, i != 0, reader, alloc);
                }
            }
            reader->ReadSequenceEnd();

            promiseInfo->RejectReactionCount = reader->ReadLengthValue(true);
            promiseInfo->RejectReactions = nullptr;
            reader->ReadSequenceStart_WDefaultKey(true);
            if(promiseInfo->RejectReactionCount != 0)
            {
                promiseInfo->RejectReactions = alloc.SlabAllocateArray<NSSnapValues::SnapPromiseReactionInfo>(promiseInfo->RejectReactionCount);

                for(uint32 i = 0; i < promiseInfo->RejectReactionCount; ++i)
                {
                    NSSnapValues::ParsePromiseReactionInfo(promiseInfo->RejectReactions + i, i != 0, reader, alloc);
                }
            }
            reader->ReadSequenceEnd();

            SnapObjectSetAddtlInfoAs<SnapPromiseInfo*, SnapObjectType::SnapPromiseObject>(snpObject, promiseInfo);
        }


#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapPromiseInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const SnapPromiseInfo* promiseInfo1 = SnapObjectGetAddtlInfoAs<SnapPromiseInfo*, SnapObjectType::SnapPromiseObject>(sobj1);
            const SnapPromiseInfo* promiseInfo2 = SnapObjectGetAddtlInfoAs<SnapPromiseInfo*, SnapObjectType::SnapPromiseObject>(sobj2);

            NSSnapValues::AssertSnapEquivTTDVar_Special(promiseInfo1->Result, promiseInfo2->Result, compareMap, _u("result"));

            compareMap.DiagnosticAssert(promiseInfo1->ResolveReactionCount == promiseInfo2->ResolveReactionCount);
            for(uint32 i = 0; i < promiseInfo1->ResolveReactionCount; ++i)
            {
                NSSnapValues::AssertSnapEquiv(promiseInfo1->ResolveReactions + i, promiseInfo2->ResolveReactions + i, compareMap);
            }

            compareMap.DiagnosticAssert(promiseInfo1->RejectReactionCount == promiseInfo2->RejectReactionCount);
            for(uint32 i = 0; i < promiseInfo1->RejectReactionCount; ++i)
            {
                NSSnapValues::AssertSnapEquiv(promiseInfo1->RejectReactions + i, promiseInfo2->RejectReactions + i, compareMap);
            }
        }
#endif

        ////
        //PromiseResolveOrRejectFunction Info

        Js::RecyclableObject* DoObjectInflation_SnapPromiseResolveOrRejectFunctionInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            const SnapPromiseResolveOrRejectFunctionInfo* rrfInfo = SnapObjectGetAddtlInfoAs<SnapPromiseResolveOrRejectFunctionInfo*, SnapObjectType::SnapPromiseResolveOrRejectFunctionObject>(snpObject);
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            Js::RecyclableObject* promise = inflator->LookupObject(rrfInfo->PromiseId);

            if(!inflator->IsPromiseInfoDefined<Js::JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper>(rrfInfo->AlreadyResolvedWrapperId))
            {
                Js::JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper* wrapper = ctx->GetLibrary()->CreateAlreadyDefinedWrapper_TTD(rrfInfo->AlreadyResolvedValue);
                inflator->AddInflatedPromiseInfo<Js::JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper>(rrfInfo->AlreadyResolvedWrapperId, wrapper);
            }
            Js::JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper* alreadyResolved = inflator->LookupInflatedPromiseInfo<Js::JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper>(rrfInfo->AlreadyResolvedWrapperId);

            return ctx->GetLibrary()->CreatePromiseResolveOrRejectFunction_TTD(promise, rrfInfo->IsReject, alreadyResolved);
        }

        void EmitAddtlInfo_SnapPromiseResolveOrRejectFunctionInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapPromiseResolveOrRejectFunctionInfo* rrfInfo = SnapObjectGetAddtlInfoAs<SnapPromiseResolveOrRejectFunctionInfo*, SnapObjectType::SnapPromiseResolveOrRejectFunctionObject>(snpObject);

            writer->WriteAddr(NSTokens::Key::ptrIdVal, rrfInfo->PromiseId, NSTokens::Separator::CommaSeparator);
            writer->WriteBool(NSTokens::Key::boolVal, rrfInfo->IsReject, NSTokens::Separator::CommaSeparator);

            writer->WriteAddr(NSTokens::Key::ptrIdVal, rrfInfo->AlreadyResolvedWrapperId, NSTokens::Separator::CommaSeparator);
            writer->WriteBool(NSTokens::Key::boolVal, rrfInfo->AlreadyResolvedValue, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapPromiseResolveOrRejectFunctionInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapPromiseResolveOrRejectFunctionInfo* rrfInfo = alloc.SlabAllocateStruct<SnapPromiseResolveOrRejectFunctionInfo>();

            rrfInfo->PromiseId = reader->ReadAddr(NSTokens::Key::ptrIdVal, true);
            rrfInfo->IsReject = reader->ReadBool(NSTokens::Key::boolVal, true);

            rrfInfo->AlreadyResolvedWrapperId = reader->ReadAddr(NSTokens::Key::ptrIdVal, true);
            rrfInfo->AlreadyResolvedValue = reader->ReadBool(NSTokens::Key::boolVal, true);

            SnapObjectSetAddtlInfoAs<SnapPromiseResolveOrRejectFunctionInfo*, SnapObjectType::SnapPromiseResolveOrRejectFunctionObject>(snpObject, rrfInfo);
        }


#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapPromiseResolveOrRejectFunctionInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            SnapPromiseResolveOrRejectFunctionInfo* rrfInfo1 = SnapObjectGetAddtlInfoAs<SnapPromiseResolveOrRejectFunctionInfo*, SnapObjectType::SnapPromiseResolveOrRejectFunctionObject>(sobj1);
            SnapPromiseResolveOrRejectFunctionInfo* rrfInfo2 = SnapObjectGetAddtlInfoAs<SnapPromiseResolveOrRejectFunctionInfo*, SnapObjectType::SnapPromiseResolveOrRejectFunctionObject>(sobj2);

            compareMap.CheckConsistentAndAddPtrIdMapping_Special(rrfInfo1->PromiseId, rrfInfo2->PromiseId, _u("promise"));

            compareMap.DiagnosticAssert(rrfInfo1->IsReject == rrfInfo2->IsReject);
            compareMap.DiagnosticAssert(rrfInfo1->AlreadyResolvedValue == rrfInfo2->AlreadyResolvedValue);

            compareMap.CheckConsistentAndAddPtrIdMapping_NoEnqueue(rrfInfo1->AlreadyResolvedWrapperId, rrfInfo2->AlreadyResolvedWrapperId);
        }
#endif

        ////
        //ReactionTaskFunction Info
        Js::RecyclableObject* DoObjectInflation_SnapPromiseReactionTaskFunctionInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            const SnapPromiseReactionTaskFunctionInfo* rInfo = SnapObjectGetAddtlInfoAs<SnapPromiseReactionTaskFunctionInfo*, SnapObjectType::SnapPromiseReactionTaskFunctionObject>(snpObject);
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            Js::JavascriptPromiseReaction* reaction = NSSnapValues::InflatePromiseReactionInfo(&rInfo->Reaction, ctx, inflator);
            Js::Var argument = inflator->InflateTTDVar(rInfo->Argument);

            return ctx->GetLibrary()->CreatePromiseReactionTaskFunction_TTD(reaction, argument);
        }

        void EmitAddtlInfo_SnapPromiseReactionTaskFunctionInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapPromiseReactionTaskFunctionInfo* rInfo = SnapObjectGetAddtlInfoAs<SnapPromiseReactionTaskFunctionInfo*, SnapObjectType::SnapPromiseReactionTaskFunctionObject>(snpObject);

            writer->WriteKey(NSTokens::Key::entry, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(rInfo->Argument, writer, NSTokens::Separator::NoSeparator);

            writer->WriteKey(NSTokens::Key::reaction, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitPromiseReactionInfo(&rInfo->Reaction, writer, NSTokens::Separator::NoSeparator);
        }

        void ParseAddtlInfo_SnapPromiseReactionTaskFunctionInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapPromiseReactionTaskFunctionInfo* rInfo = alloc.SlabAllocateStruct<SnapPromiseReactionTaskFunctionInfo>();

            reader->ReadKey(NSTokens::Key::entry, true);
            rInfo->Argument = NSSnapValues::ParseTTDVar(false, reader);

            reader->ReadKey(NSTokens::Key::reaction, true);
            NSSnapValues::ParsePromiseReactionInfo(&rInfo->Reaction, false, reader, alloc);

            SnapObjectSetAddtlInfoAs<SnapPromiseReactionTaskFunctionInfo*, SnapObjectType::SnapPromiseReactionTaskFunctionObject>(snpObject, rInfo);
        }


#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapPromiseReactionTaskFunctionInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            SnapPromiseReactionTaskFunctionInfo* rInfo1 = SnapObjectGetAddtlInfoAs<SnapPromiseReactionTaskFunctionInfo*, SnapObjectType::SnapPromiseReactionTaskFunctionObject>(sobj1);
            SnapPromiseReactionTaskFunctionInfo* rInfo2 = SnapObjectGetAddtlInfoAs<SnapPromiseReactionTaskFunctionInfo*, SnapObjectType::SnapPromiseReactionTaskFunctionObject>(sobj2);

            NSSnapValues::AssertSnapEquivTTDVar_Special(rInfo1->Argument, rInfo2->Argument, compareMap, _u("argument"));
            NSSnapValues::AssertSnapEquiv(&(rInfo1->Reaction), &(rInfo2->Reaction), compareMap);
        }
#endif

        ////
        //AllResolveElementFunctionObject Info
        Js::RecyclableObject* DoObjectInflation_SnapPromiseAllResolveElementFunctionInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            const SnapPromiseAllResolveElementFunctionInfo* aInfo = SnapObjectGetAddtlInfoAs<SnapPromiseAllResolveElementFunctionInfo*, SnapObjectType::SnapPromiseAllResolveElementFunctionObject>(snpObject);
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            Js::JavascriptPromiseCapability* capabilities = InflatePromiseCapabilityInfo(&aInfo->Capabilities, ctx, inflator);

            if(!inflator->IsPromiseInfoDefined<Js::JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper>(aInfo->RemainingElementsWrapperId))
            {
                Js::JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper* remainingWrapper = ctx->GetLibrary()->CreateRemainingElementsWrapper_TTD(ctx, aInfo->RemainingElementsValue);
                inflator->AddInflatedPromiseInfo(aInfo->RemainingElementsWrapperId, remainingWrapper);
            }
            Js::JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper* wrapper = inflator->LookupInflatedPromiseInfo<Js::JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper>(aInfo->RemainingElementsWrapperId);

            Js::RecyclableObject* values = inflator->LookupObject(aInfo->Values);

            return ctx->GetLibrary()->CreatePromiseAllResolveElementFunction_TTD(capabilities, aInfo->Index, wrapper, values, aInfo->AlreadyCalled);
        }

        void EmitAddtlInfo_SnapPromiseAllResolveElementFunctionInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapPromiseAllResolveElementFunctionInfo* aInfo = SnapObjectGetAddtlInfoAs<SnapPromiseAllResolveElementFunctionInfo*, SnapObjectType::SnapPromiseAllResolveElementFunctionObject>(snpObject);

            writer->WriteKey(NSTokens::Key::entry, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitPromiseCapabilityInfo(&aInfo->Capabilities, writer, NSTokens::Separator::NoSeparator);

            writer->WriteUInt32(NSTokens::Key::u32Val, aInfo->Index, NSTokens::Separator::CommaSeparator);
            writer->WriteAddr(NSTokens::Key::ptrIdVal, aInfo->RemainingElementsWrapperId, NSTokens::Separator::CommaSeparator);
            writer->WriteUInt32(NSTokens::Key::u32Val, aInfo->RemainingElementsValue, NSTokens::Separator::CommaSeparator);

            writer->WriteAddr(NSTokens::Key::ptrIdVal, aInfo->Values, NSTokens::Separator::CommaSeparator);

            writer->WriteBool(NSTokens::Key::boolVal, aInfo->AlreadyCalled, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapPromiseAllResolveElementFunctionInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapPromiseAllResolveElementFunctionInfo* aInfo = alloc.SlabAllocateStruct<SnapPromiseAllResolveElementFunctionInfo>();

            reader->ReadKey(NSTokens::Key::entry, true);
            NSSnapValues::ParsePromiseCapabilityInfo(&aInfo->Capabilities, false, reader, alloc);

            aInfo->Index = reader->ReadUInt32(NSTokens::Key::u32Val, true);
            aInfo->RemainingElementsWrapperId = reader->ReadAddr(NSTokens::Key::ptrIdVal, true);
            aInfo->RemainingElementsValue = reader->ReadUInt32(NSTokens::Key::u32Val, true);

            aInfo->Values = reader->ReadAddr(NSTokens::Key::ptrIdVal, true);

            aInfo->AlreadyCalled = reader->ReadBool(NSTokens::Key::boolVal, true);

            SnapObjectSetAddtlInfoAs<SnapPromiseAllResolveElementFunctionInfo*, SnapObjectType::SnapPromiseAllResolveElementFunctionObject>(snpObject, aInfo);
        }


#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapPromiseAllResolveElementFunctionInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            SnapPromiseAllResolveElementFunctionInfo* aInfo1 = SnapObjectGetAddtlInfoAs<SnapPromiseAllResolveElementFunctionInfo*, SnapObjectType::SnapPromiseAllResolveElementFunctionObject>(sobj1);
            SnapPromiseAllResolveElementFunctionInfo* aInfo2 = SnapObjectGetAddtlInfoAs<SnapPromiseAllResolveElementFunctionInfo*, SnapObjectType::SnapPromiseAllResolveElementFunctionObject>(sobj2);

            NSSnapValues::AssertSnapEquiv(&aInfo1->Capabilities, &aInfo2->Capabilities, compareMap);

            compareMap.DiagnosticAssert(aInfo1->Index == aInfo2->Index);
            compareMap.DiagnosticAssert(aInfo1->RemainingElementsValue == aInfo2->RemainingElementsValue);
            compareMap.DiagnosticAssert(aInfo1->AlreadyCalled == aInfo2->AlreadyCalled);

            compareMap.CheckConsistentAndAddPtrIdMapping_Special(aInfo1->Values, aInfo2->Values, _u("values"));

            compareMap.CheckConsistentAndAddPtrIdMapping_NoEnqueue(aInfo1->RemainingElementsWrapperId, aInfo2->RemainingElementsWrapperId);
        }
#endif

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapBoxedValue(const SnapObject* snpObject, InflateMap* inflator)
        {
            //Boxed values are not too common and have special internal state so it seems easiest to always re-create them.
            //We can re-evaluate this choice later if needed.

            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            return ctx->GetLibrary()->CreateDefaultBoxedObject_TTD(snpObject->SnapType->JsTypeId);
        }

        void DoAddtlValueInstantiation_SnapBoxedValue(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            TTDVar snapBoxedVar = SnapObjectGetAddtlInfoAs<TTDVar, SnapObjectType::SnapBoxedValueObject>(snpObject);
            Js::Var jsvar = (snapBoxedVar != nullptr) ? inflator->InflateTTDVar(snapBoxedVar) : nullptr;

            ctx->GetLibrary()->SetBoxedObjectValue_TTD(obj, jsvar);
        }

        void EmitAddtlInfo_SnapBoxedValue(const SnapObject* snpObject, FileWriter* writer)
        {
            TTDVar snapBoxedVar = SnapObjectGetAddtlInfoAs<TTDVar, SnapObjectType::SnapBoxedValueObject>(snpObject);

            writer->WriteKey(NSTokens::Key::boxedInfo, NSTokens::Separator::CommaAndBigSpaceSeparator);
            NSSnapValues::EmitTTDVar(snapBoxedVar, writer, NSTokens::Separator::NoSeparator);
        }

        void ParseAddtlInfo_SnapBoxedValue(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            reader->ReadKey(NSTokens::Key::boxedInfo, true);
            TTDVar snapVar = NSSnapValues::ParseTTDVar(false, reader);

            SnapObjectSetAddtlInfoAs<TTDVar, SnapObjectType::SnapBoxedValueObject>(snpObject, snapVar);
        }


#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapBoxedValue(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            TTDVar snapBoxedVar1 = SnapObjectGetAddtlInfoAs<TTDVar, SnapObjectType::SnapBoxedValueObject>(sobj1);
            TTDVar snapBoxedVar2 = SnapObjectGetAddtlInfoAs<TTDVar, SnapObjectType::SnapBoxedValueObject>(sobj2);

            NSSnapValues::AssertSnapEquivTTDVar_Special(snapBoxedVar1, snapBoxedVar2, compareMap, _u("boxedVar"));
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapDate(const SnapObject* snpObject, InflateMap* inflator)
        {
            //Dates are not too common and have some mutable state so it seems easiest to always re-create them.
            //We can re-evaluate this choice later if needed.

            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            double* dateInfo = SnapObjectGetAddtlInfoAs<double*, SnapObjectType::SnapDateObject>(snpObject);

            return ctx->GetLibrary()->CreateDate_TTD(*dateInfo);
        }

        void EmitAddtlInfo_SnapDate(const SnapObject* snpObject, FileWriter* writer)
        {
            double* dateInfo = SnapObjectGetAddtlInfoAs<double*, SnapObjectType::SnapDateObject>(snpObject);
            writer->WriteDouble(NSTokens::Key::doubleVal, *dateInfo, NSTokens::Separator::CommaAndBigSpaceSeparator);
        }

        void ParseAddtlInfo_SnapDate(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            double* dateInfo = alloc.SlabAllocateStruct<double>();
            *dateInfo = reader->ReadDouble(NSTokens::Key::doubleVal, true);

            SnapObjectSetAddtlInfoAs<double*, SnapObjectType::SnapDateObject>(snpObject, dateInfo);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapDate(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const double* dateInfo1 = SnapObjectGetAddtlInfoAs<double*, SnapObjectType::SnapDateObject>(sobj1);
            const double* dateInfo2 = SnapObjectGetAddtlInfoAs<double*, SnapObjectType::SnapDateObject>(sobj2);

            compareMap.DiagnosticAssert(NSSnapValues::CheckSnapEquivTTDDouble(*dateInfo1, *dateInfo2));
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapRegexInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            //Regexes are not too common and have some mutable state so it seems easiest to always re-create them.
            //We can re-evaluate this choice later if needed.

            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapRegexInfo* regexInfo = SnapObjectGetAddtlInfoAs<SnapRegexInfo*, SnapObjectType::SnapRegexObject>(snpObject);

            Js::Var lastVar = (regexInfo->LastIndexVar != nullptr) ? inflator->InflateTTDVar(regexInfo->LastIndexVar) : nullptr;

            return ctx->GetLibrary()->CreateRegex_TTD(regexInfo->RegexStr.Contents, regexInfo->RegexStr.Length, regexInfo->Flags, regexInfo->LastIndexOrFlag, lastVar);
        }

        void EmitAddtlInfo_SnapRegexInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapRegexInfo* regexInfo = SnapObjectGetAddtlInfoAs<SnapRegexInfo*, SnapObjectType::SnapRegexObject>(snpObject);

            writer->WriteString(NSTokens::Key::stringVal, regexInfo->RegexStr, NSTokens::Separator::CommaAndBigSpaceSeparator);

            writer->WriteTag<UnifiedRegex::RegexFlags>(NSTokens::Key::attributeFlags, regexInfo->Flags, NSTokens::Separator::CommaSeparator);
            writer->WriteUInt32(NSTokens::Key::u32Val, regexInfo->LastIndexOrFlag, NSTokens::Separator::CommaSeparator);

            writer->WriteKey(NSTokens::Key::ttdVarTag, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(regexInfo->LastIndexVar, writer, NSTokens::Separator::NoSeparator);
        }

        void ParseAddtlInfo_SnapRegexInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapRegexInfo* regexInfo = alloc.SlabAllocateStruct<SnapRegexInfo>();

            reader->ReadString(NSTokens::Key::stringVal, alloc, regexInfo->RegexStr, true);

            regexInfo->Flags = reader->ReadTag<UnifiedRegex::RegexFlags>(NSTokens::Key::attributeFlags, true);
            regexInfo->LastIndexOrFlag = reader->ReadUInt32(NSTokens::Key::u32Val, true);

            reader->ReadKey(NSTokens::Key::ttdVarTag, true);
            regexInfo->LastIndexVar = NSSnapValues::ParseTTDVar(false, reader);

            SnapObjectSetAddtlInfoAs<SnapRegexInfo*, SnapObjectType::SnapRegexObject>(snpObject, regexInfo);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapRegexInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const SnapRegexInfo* regexInfo1 = SnapObjectGetAddtlInfoAs<SnapRegexInfo*, SnapObjectType::SnapRegexObject>(sobj1);
            const SnapRegexInfo* regexInfo2 = SnapObjectGetAddtlInfoAs<SnapRegexInfo*, SnapObjectType::SnapRegexObject>(sobj2);

            compareMap.DiagnosticAssert(TTStringEQForDiagnostics(regexInfo1->RegexStr, regexInfo2->RegexStr));
            compareMap.DiagnosticAssert(regexInfo1->Flags == regexInfo2->Flags);
            compareMap.DiagnosticAssert(regexInfo1->LastIndexOrFlag == regexInfo2->LastIndexOrFlag);
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapError(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            //
            //TODO: This is not great we just create a generic error
            //
            return ctx->GetLibrary()->CreateError_TTD();
        }

        //////////////////

        int32 SnapArrayInfo_InflateValue(int32 value, InflateMap* inflator) { return value; }
        void SnapArrayInfo_EmitValue(int32 value, FileWriter* writer) { writer->WriteInt32(NSTokens::Key::i32Val, value, NSTokens::Separator::CommaSeparator); }
        void SnapArrayInfo_ParseValue(int32* into, FileReader* reader, SlabAllocator& alloc) { *into = reader->ReadInt32(NSTokens::Key::i32Val, true); }

        double SnapArrayInfo_InflateValue(double value, InflateMap* inflator) { return value; }
        void SnapArrayInfo_EmitValue(double value, FileWriter* writer) { writer->WriteDouble(NSTokens::Key::doubleVal, value, NSTokens::Separator::CommaSeparator); }
        void SnapArrayInfo_ParseValue(double* into, FileReader* reader, SlabAllocator& alloc) { *into = reader->ReadDouble(NSTokens::Key::doubleVal, true); }

        Js::Var SnapArrayInfo_InflateValue(TTDVar value, InflateMap* inflator)
        {
            return inflator->InflateTTDVar(value);
        }

        void SnapArrayInfo_EmitValue(TTDVar value, FileWriter* writer)
        {
            writer->WriteKey(NSTokens::Key::ptrIdVal, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(value, writer, NSTokens::Separator::NoSeparator);
        }

        void SnapArrayInfo_ParseValue(TTDVar* into, FileReader* reader, SlabAllocator& alloc)
        {
            reader->ReadKey(NSTokens::Key::ptrIdVal, true);
            *into = NSSnapValues::ParseTTDVar(false, reader);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void SnapArrayInfo_EquivValue(int32 val1, int32 val2, TTDCompareMap& compareMap, int32 i)
        {
            compareMap.DiagnosticAssert(val1 == val2);
        }

        void SnapArrayInfo_EquivValue(double val1, double val2, TTDCompareMap& compareMap, int32 i)
        {
            compareMap.DiagnosticAssert(val1 == val2);
        }

        void SnapArrayInfo_EquivValue(TTDVar val1, TTDVar val2, TTDCompareMap& compareMap, int32 i)
        {
            NSSnapValues::AssertSnapEquivTTDVar_Array(val1, val2, compareMap, i);
        }
#endif

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapES5ArrayInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            return ctx->GetLibrary()->CreateES5Array_TTD();
        }

        void DoAddtlValueInstantiation_SnapES5ArrayInfo(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        {
            SnapES5ArrayInfo* es5Info = SnapObjectGetAddtlInfoAs<SnapES5ArrayInfo*, SnapObjectType::SnapES5ArrayObject>(snpObject);

            Js::JavascriptArray* arrayObj = static_cast<Js::JavascriptArray*>(obj);
            DoAddtlValueInstantiation_SnapArrayInfoCore<TTDVar, Js::Var>(es5Info->BasicArrayData, arrayObj, inflator);

            for(uint32 i = 0; i < es5Info->GetterSetterCount; ++i)
            {
                const SnapES5ArrayGetterSetterEntry* entry = es5Info->GetterSetterEntries + i;

                Js::Var getter = nullptr;
                if(entry->Getter != nullptr)
                {
                    getter = inflator->InflateTTDVar(entry->Getter);
                }

                Js::Var setter = nullptr;
                if(entry->Setter != nullptr)
                {
                    setter = inflator->InflateTTDVar(entry->Setter);
                }

                if(getter != nullptr || setter != nullptr)
                {
                    arrayObj->SetItemAccessors(entry->Index, getter, setter);
                }

                arrayObj->SetItemAttributes(entry->Index, entry->Attributes);
            }

            //do length writable as needed
            Js::JavascriptLibrary::SetLengthWritableES5Array_TTD(arrayObj, es5Info->IsLengthWritable);
        }

        void EmitAddtlInfo_SnapES5ArrayInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapES5ArrayInfo* es5Info = SnapObjectGetAddtlInfoAs<SnapES5ArrayInfo*, SnapObjectType::SnapES5ArrayObject>(snpObject);

            writer->WriteLengthValue(es5Info->GetterSetterCount, NSTokens::Separator::CommaSeparator);
            writer->WriteBool(NSTokens::Key::boolVal, es5Info->IsLengthWritable, NSTokens::Separator::CommaSeparator);

            writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
            for(uint32 i = 0; i < es5Info->GetterSetterCount; ++i)
            {
                NSTokens::Separator sep = (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator;
                const SnapES5ArrayGetterSetterEntry* entry = es5Info->GetterSetterEntries + i;

                writer->WriteRecordStart(sep);

                writer->WriteUInt32(NSTokens::Key::index, entry->Index);
                writer->WriteUInt32(NSTokens::Key::attributeFlags, entry->Attributes, NSTokens::Separator::CommaSeparator);

                writer->WriteKey(NSTokens::Key::getterEntry, NSTokens::Separator::CommaSeparator);
                NSSnapValues::EmitTTDVar(entry->Getter, writer, NSTokens::Separator::NoSeparator);

                writer->WriteKey(NSTokens::Key::setterEntry, NSTokens::Separator::CommaSeparator);
                NSSnapValues::EmitTTDVar(entry->Setter, writer, NSTokens::Separator::NoSeparator);

                writer->WriteRecordEnd();
            }
            writer->WriteSequenceEnd();

            EmitAddtlInfo_SnapArrayInfoCore<TTDVar>(es5Info->BasicArrayData, writer);
        }

        void ParseAddtlInfo_SnapES5ArrayInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapES5ArrayInfo* es5Info = alloc.SlabAllocateStruct<SnapES5ArrayInfo>();

            es5Info->GetterSetterCount = reader->ReadLengthValue(true);
            es5Info->IsLengthWritable = reader->ReadBool(NSTokens::Key::boolVal, true);

            if(es5Info->GetterSetterCount == 0)
            {
                es5Info->GetterSetterEntries = nullptr;
            }
            else
            {
                es5Info->GetterSetterEntries = alloc.SlabAllocateArray<SnapES5ArrayGetterSetterEntry>(es5Info->GetterSetterCount);
            }

            reader->ReadSequenceStart_WDefaultKey(true);
            for(uint32 i = 0; i < es5Info->GetterSetterCount; ++i)
            {
                SnapES5ArrayGetterSetterEntry* entry = es5Info->GetterSetterEntries + i;

                reader->ReadRecordStart(i != 0);

                entry->Index = reader->ReadUInt32(NSTokens::Key::index);
                entry->Attributes = (Js::PropertyAttributes)reader->ReadUInt32(NSTokens::Key::attributeFlags, true);

                reader->ReadKey(NSTokens::Key::getterEntry, true);
                entry->Getter = NSSnapValues::ParseTTDVar(false, reader);

                reader->ReadKey(NSTokens::Key::setterEntry, true);
                entry->Setter = NSSnapValues::ParseTTDVar(false, reader);

                reader->ReadRecordEnd();
            }
            reader->ReadSequenceEnd();

            es5Info->BasicArrayData = ParseAddtlInfo_SnapArrayInfoCore<TTDVar>(reader, alloc);

            SnapObjectSetAddtlInfoAs<SnapES5ArrayInfo*, SnapObjectType::SnapES5ArrayObject>(snpObject, es5Info);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapES5ArrayInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            SnapES5ArrayInfo* es5Info1 = SnapObjectGetAddtlInfoAs<SnapES5ArrayInfo*, SnapObjectType::SnapES5ArrayObject>(sobj1);
            SnapES5ArrayInfo* es5Info2 = SnapObjectGetAddtlInfoAs<SnapES5ArrayInfo*, SnapObjectType::SnapES5ArrayObject>(sobj2);

            compareMap.DiagnosticAssert(es5Info1->GetterSetterCount == es5Info2->GetterSetterCount);
            compareMap.DiagnosticAssert(es5Info1->IsLengthWritable == es5Info2->IsLengthWritable);

            for(uint32 i = 0; i < es5Info1->GetterSetterCount; ++i)
            {
                const SnapES5ArrayGetterSetterEntry* entry1 = es5Info1->GetterSetterEntries + i;
                const SnapES5ArrayGetterSetterEntry* entry2 = es5Info2->GetterSetterEntries + i;

                compareMap.DiagnosticAssert(entry1->Index == entry2->Index);
                compareMap.DiagnosticAssert(entry1->Attributes == entry2->Attributes);

                NSSnapValues::AssertSnapEquivTTDVar_SpecialArray(entry1->Getter, entry2->Getter, compareMap, _u("es5Getter"), entry1->Index);
                NSSnapValues::AssertSnapEquivTTDVar_SpecialArray(entry1->Setter, entry2->Setter, compareMap, _u("es5Setter"), entry1->Index);
            }

            compareMap.DiagnosticAssert(es5Info1->BasicArrayData->Length == es5Info2->BasicArrayData->Length);

            AssertSnapEquiv_SnapArrayInfoCore<TTDVar>(es5Info1->BasicArrayData->Data, es5Info2->BasicArrayData->Data, compareMap);
        }
#endif

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapArrayBufferInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            //ArrayBuffers can change on us so seems easiest to always re-create them.
            //We can re-evaluate this choice later if needed.

            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapArrayBufferInfo* buffInfo = SnapObjectGetAddtlInfoAs<SnapArrayBufferInfo*, SnapObjectType::SnapArrayBufferObject>(snpObject);

            Js::ArrayBuffer* abuff = ctx->GetLibrary()->CreateArrayBuffer(buffInfo->Length);
            TTDAssert(abuff->GetByteLength() == buffInfo->Length, "Something is wrong with our sizes.");

            js_memcpy_s(abuff->GetBuffer(), abuff->GetByteLength(), buffInfo->Buff, buffInfo->Length);

            return abuff;
        }

        void EmitAddtlInfo_SnapArrayBufferInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapArrayBufferInfo* buffInfo = SnapObjectGetAddtlInfoAs<SnapArrayBufferInfo*, SnapObjectType::SnapArrayBufferObject>(snpObject);

            writer->WriteLengthValue(buffInfo->Length, NSTokens::Separator::CommaAndBigSpaceSeparator);

            if(buffInfo->Length > 0)
            {
                writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
                for(uint32 i = 0; i < buffInfo->Length; ++i)
                {
                    writer->WriteNakedByte(buffInfo->Buff[i], i != 0 ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator);
                }
                writer->WriteSequenceEnd();
            }
        }

        void ParseAddtlInfo_SnapArrayBufferInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapArrayBufferInfo* buffInfo = alloc.SlabAllocateStruct<SnapArrayBufferInfo>();

            buffInfo->Length = reader->ReadLengthValue(true);

            if(buffInfo->Length == 0)
            {
                buffInfo->Buff = nullptr;
            }
            else
            {
                buffInfo->Buff = alloc.SlabAllocateArray<byte>(buffInfo->Length);

                reader->ReadSequenceStart_WDefaultKey(true);
                for(uint32 i = 0; i < buffInfo->Length; ++i)
                {
                    buffInfo->Buff[i] = reader->ReadNakedByte(i != 0);
                }
                reader->ReadSequenceEnd();
            }

            SnapObjectSetAddtlInfoAs<SnapArrayBufferInfo*, SnapObjectType::SnapArrayBufferObject>(snpObject, buffInfo);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapArrayBufferInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const SnapArrayBufferInfo* buffInfo1 = SnapObjectGetAddtlInfoAs<SnapArrayBufferInfo*, SnapObjectType::SnapArrayBufferObject>(sobj1);
            const SnapArrayBufferInfo* buffInfo2 = SnapObjectGetAddtlInfoAs<SnapArrayBufferInfo*, SnapObjectType::SnapArrayBufferObject>(sobj2);

            compareMap.DiagnosticAssert(buffInfo1->Length == buffInfo2->Length);

            //
            //Pending buffers cannot be accessed by the program until they are off the pending lists.
            //So we do not force the updates in closer sync than this and they may not be updated in sync so (as long as they are pending) in both.
            //
            if(compareMap.H1PendingAsyncModBufferSet.Contains(sobj1->ObjectPtrId) || compareMap.H2PendingAsyncModBufferSet.Contains(sobj2->ObjectPtrId))
            {
                compareMap.DiagnosticAssert(compareMap.H1PendingAsyncModBufferSet.Contains(sobj1->ObjectPtrId) && compareMap.H2PendingAsyncModBufferSet.Contains(sobj2->ObjectPtrId));
            }
            else
            {
                for(uint32 i = 0; i < buffInfo1->Length; ++i)
                {
                    compareMap.DiagnosticAssert(buffInfo1->Buff[i] == buffInfo2->Buff[i]);
                }
            }
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapTypedArrayInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            //I am lazy and always re-create typed arrays.
            //We can re-evaluate this choice later if needed.

            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapTypedArrayInfo* typedArrayInfo = SnapObjectGetAddtlInfoAs<SnapTypedArrayInfo*, SnapObjectType::SnapTypedArrayObject>(snpObject);

            Js::JavascriptLibrary* jslib = ctx->GetLibrary();
            Js::ArrayBuffer* arrayBuffer = Js::ArrayBuffer::FromVar(inflator->LookupObject(typedArrayInfo->ArrayBufferAddr));

            Js::Var tab = nullptr;
            switch(snpObject->SnapType->JsTypeId)
            {
            case Js::TypeIds_Int8Array:
                tab = Js::Int8Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Uint8Array:
                tab = Js::Uint8Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Uint8ClampedArray:
                tab = Js::Uint8ClampedArray::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Int16Array:
                tab = Js::Int16Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Uint16Array:
                tab = Js::Uint16Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Int32Array:
                tab = Js::Int32Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Uint32Array:
                tab = Js::Uint32Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Float32Array:
                tab = Js::Float32Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Float64Array:
                tab = Js::Float64Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Int64Array:
                tab = Js::Int64Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_Uint64Array:
                tab = Js::Uint64Array::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_BoolArray:
                tab = Js::BoolArray::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            case Js::TypeIds_CharArray:
                tab = Js::CharArray::Create(arrayBuffer, typedArrayInfo->ByteOffset, typedArrayInfo->Length, jslib);
                break;
            default:
                TTDAssert(false, "Not a typed array!");
                break;
            }

            return Js::RecyclableObject::FromVar(tab);
        }

        void EmitAddtlInfo_SnapTypedArrayInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapTypedArrayInfo* typedArrayInfo = SnapObjectGetAddtlInfoAs<SnapTypedArrayInfo*, SnapObjectType::SnapTypedArrayObject>(snpObject);

            writer->WriteUInt32(NSTokens::Key::offset, typedArrayInfo->ByteOffset, NSTokens::Separator::CommaAndBigSpaceSeparator);
            writer->WriteLengthValue(typedArrayInfo->Length, NSTokens::Separator::CommaSeparator);
            writer->WriteAddr(NSTokens::Key::objectId, typedArrayInfo->ArrayBufferAddr, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapTypedArrayInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapTypedArrayInfo* typedArrayInfo = alloc.SlabAllocateStruct<SnapTypedArrayInfo>();

            typedArrayInfo->ByteOffset = reader->ReadUInt32(NSTokens::Key::offset, true);
            typedArrayInfo->Length = reader->ReadLengthValue(true);
            typedArrayInfo->ArrayBufferAddr = reader->ReadAddr(NSTokens::Key::objectId, true);

            SnapObjectSetAddtlInfoAs<SnapTypedArrayInfo*, SnapObjectType::SnapTypedArrayObject>(snpObject, typedArrayInfo);
        }


#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapTypedArrayInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const SnapTypedArrayInfo* typedArrayInfo1 = SnapObjectGetAddtlInfoAs<SnapTypedArrayInfo*, SnapObjectType::SnapTypedArrayObject>(sobj1);
            const SnapTypedArrayInfo* typedArrayInfo2 = SnapObjectGetAddtlInfoAs<SnapTypedArrayInfo*, SnapObjectType::SnapTypedArrayObject>(sobj2);

            compareMap.DiagnosticAssert(typedArrayInfo1->ByteOffset == typedArrayInfo2->ByteOffset);
            compareMap.DiagnosticAssert(typedArrayInfo1->Length == typedArrayInfo2->Length);

            compareMap.CheckConsistentAndAddPtrIdMapping_Special(typedArrayInfo1->ArrayBufferAddr, typedArrayInfo2->ArrayBufferAddr, _u("arrayBuffer"));
        }
#endif

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapSetInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            if(snpObject->SnapType->JsTypeId == Js::TypeIds_Set)
            {
                return ctx->GetLibrary()->CreateSet_TTD();
            }
            else
            {
                return ctx->GetLibrary()->CreateWeakSet_TTD();
            }
        }

        void DoAddtlValueInstantiation_SnapSetInfo(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        {
            SnapSetInfo* setInfo = SnapObjectGetAddtlInfoAs<SnapSetInfo*, SnapObjectType::SnapSetObject>(snpObject);

            if(snpObject->SnapType->JsTypeId == Js::TypeIds_Set)
            {
                Js::JavascriptSet* sobj = (Js::JavascriptSet*)obj;
                for(uint32 i = 0; i < setInfo->SetSize; ++i)
                {
                    Js::Var val = inflator->InflateTTDVar(setInfo->SetValueArray[i]);
                    Js::JavascriptLibrary::AddSetElementInflate_TTD(sobj, val);
                }
            }
            else
            {
                Js::JavascriptWeakSet* sobj = (Js::JavascriptWeakSet*)obj;
                for(uint32 i = 0; i < setInfo->SetSize; ++i)
                {
                    Js::Var val = inflator->InflateTTDVar(setInfo->SetValueArray[i]);
                    Js::JavascriptLibrary::AddWeakSetElementInflate_TTD(sobj, val);
                }
            }
        }

        void EmitAddtlInfo_SnapSetInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapSetInfo* setInfo = SnapObjectGetAddtlInfoAs<SnapSetInfo*, SnapObjectType::SnapSetObject>(snpObject);

            writer->WriteLengthValue(setInfo->SetSize, NSTokens::Separator::CommaAndBigSpaceSeparator);

            if(setInfo->SetSize > 0)
            {
                writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
                for(uint32 i = 0; i < setInfo->SetSize; ++i)
                {
                    NSSnapValues::EmitTTDVar(setInfo->SetValueArray[i], writer, i != 0 ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::BigSpaceSeparator);
                }
                writer->WriteSequenceEnd();
            }
        }

        void ParseAddtlInfo_SnapSetInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapSetInfo* setInfo = alloc.SlabAllocateStruct<SnapSetInfo>();

            setInfo->SetSize = reader->ReadLengthValue(true);
            if(setInfo->SetSize == 0)
            {
                setInfo->SetValueArray = nullptr;
            }
            else
            {
                setInfo->SetValueArray = alloc.SlabAllocateArray<TTDVar>(setInfo->SetSize);

                reader->ReadSequenceStart_WDefaultKey(true);
                for(uint32 i = 0; i < setInfo->SetSize; ++i)
                {
                    setInfo->SetValueArray[i] = NSSnapValues::ParseTTDVar(i != 0, reader);
                }
                reader->ReadSequenceEnd();
            }

            SnapObjectSetAddtlInfoAs<SnapSetInfo*, SnapObjectType::SnapSetObject>(snpObject, setInfo);
        }


#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapSetInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const SnapSetInfo* setInfo1 = SnapObjectGetAddtlInfoAs<SnapSetInfo*, SnapObjectType::SnapSetObject>(sobj1);
            const SnapSetInfo* setInfo2 = SnapObjectGetAddtlInfoAs<SnapSetInfo*, SnapObjectType::SnapSetObject>(sobj2);

            compareMap.DiagnosticAssert(setInfo1->SetSize == setInfo2->SetSize);

            //Treat weak set contents as ignorable
            if(sobj1->SnapType->JsTypeId == Js::TypeIds_Set)
            {
                for(uint32 i = 0; i < setInfo1->SetSize; ++i)
                {
                    NSSnapValues::AssertSnapEquivTTDVar_SpecialArray(setInfo1->SetValueArray[i], setInfo2->SetValueArray[i], compareMap, _u("setValues"), i);
                }
            }
        }
#endif

        Js::RecyclableObject* DoObjectInflation_SnapMapInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            if(snpObject->SnapType->JsTypeId == Js::TypeIds_Map)
            {
                return ctx->GetLibrary()->CreateMap_TTD();
            }
            else
            {
                return ctx->GetLibrary()->CreateWeakMap_TTD();
            }
        }

        void DoAddtlValueInstantiation_SnapMapInfo(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        {
            SnapMapInfo* mapInfo = SnapObjectGetAddtlInfoAs<SnapMapInfo*, SnapObjectType::SnapMapObject>(snpObject);

            if(snpObject->SnapType->JsTypeId == Js::TypeIds_Map)
            {
                Js::JavascriptMap* mobj = (Js::JavascriptMap*)obj;
                for(uint32 i = 0; i < mapInfo->MapSize; i += 2)
                {
                    Js::Var key = inflator->InflateTTDVar(mapInfo->MapKeyValueArray[i]);
                    Js::Var data = inflator->InflateTTDVar(mapInfo->MapKeyValueArray[i + 1]);
                    Js::JavascriptLibrary::AddMapElementInflate_TTD(mobj, key, data);
                }
            }
            else
            {
                Js::JavascriptWeakMap* mobj = (Js::JavascriptWeakMap*)obj;
                for(uint32 i = 0; i < mapInfo->MapSize; i += 2)
                {
                    Js::Var key = inflator->InflateTTDVar(mapInfo->MapKeyValueArray[i]);
                    Js::Var data = inflator->InflateTTDVar(mapInfo->MapKeyValueArray[i + 1]);
                    Js::JavascriptLibrary::AddWeakMapElementInflate_TTD(mobj, key, data);
                }
            }
        }

        void EmitAddtlInfo_SnapMapInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapMapInfo* mapInfo = SnapObjectGetAddtlInfoAs<SnapMapInfo*, SnapObjectType::SnapMapObject>(snpObject);

            writer->WriteLengthValue(mapInfo->MapSize, NSTokens::Separator::CommaAndBigSpaceSeparator);

            if(mapInfo->MapSize > 0)
            {
                writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
                for(uint32 i = 0; i < mapInfo->MapSize; i += 2)
                {
                    writer->WriteSequenceStart(i != 0 ? NSTokens::Separator::CommaAndBigSpaceSeparator : NSTokens::Separator::BigSpaceSeparator);
                    NSSnapValues::EmitTTDVar(mapInfo->MapKeyValueArray[i], writer, NSTokens::Separator::NoSeparator);
                    NSSnapValues::EmitTTDVar(mapInfo->MapKeyValueArray[i + 1], writer, NSTokens::Separator::CommaSeparator);
                    writer->WriteSequenceEnd();
                }
                writer->WriteSequenceEnd();
            }
        }

        void ParseAddtlInfo_SnapMapInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapMapInfo* mapInfo = alloc.SlabAllocateStruct<SnapMapInfo>();

            mapInfo->MapSize = reader->ReadLengthValue(true);
            if(mapInfo->MapSize == 0)
            {
                mapInfo->MapKeyValueArray = nullptr;
            }
            else
            {
                mapInfo->MapKeyValueArray = alloc.SlabAllocateArray<TTDVar>(mapInfo->MapSize);

                reader->ReadSequenceStart_WDefaultKey(true);
                for(uint32 i = 0; i < mapInfo->MapSize; i += 2)
                {
                    reader->ReadSequenceStart(i != 0);
                    mapInfo->MapKeyValueArray[i] = NSSnapValues::ParseTTDVar(false, reader);
                    mapInfo->MapKeyValueArray[i + 1] = NSSnapValues::ParseTTDVar(true, reader);
                    reader->ReadSequenceEnd();
                }
                reader->ReadSequenceEnd();
            }

            SnapObjectSetAddtlInfoAs<SnapMapInfo*, SnapObjectType::SnapMapObject>(snpObject, mapInfo);
        }


#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapMapInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const SnapMapInfo* mapInfo1 = SnapObjectGetAddtlInfoAs<SnapMapInfo*, SnapObjectType::SnapMapObject>(sobj1);
            const SnapMapInfo* mapInfo2 = SnapObjectGetAddtlInfoAs<SnapMapInfo*, SnapObjectType::SnapMapObject>(sobj2);

            compareMap.DiagnosticAssert(mapInfo1->MapSize == mapInfo2->MapSize);

            //Treat weak map contents as ignorable
            if(sobj1->SnapType->JsTypeId == Js::TypeIds_Map)
            {
                for(uint32 i = 0; i < mapInfo1->MapSize; i += 2)
                {
                    NSSnapValues::AssertSnapEquivTTDVar_SpecialArray(mapInfo1->MapKeyValueArray[i], mapInfo2->MapKeyValueArray[i], compareMap, _u("mapKeys"), i);
                    NSSnapValues::AssertSnapEquivTTDVar_SpecialArray(mapInfo1->MapKeyValueArray[i + 1], mapInfo2->MapKeyValueArray[i + 1], compareMap, _u("mapValues"), i);
                }
            }
        }
#endif

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapProxyInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            SnapProxyInfo* proxyInfo = SnapObjectGetAddtlInfoAs<SnapProxyInfo*, SnapObjectType::SnapProxyObject>(snpObject);

            Js::RecyclableObject* handlerObj = (proxyInfo->HandlerId != TTD_INVALID_PTR_ID) ? inflator->LookupObject(proxyInfo->HandlerId) : nullptr;
            Js::RecyclableObject* targetObj = (proxyInfo->TargetId != TTD_INVALID_PTR_ID) ? inflator->LookupObject(proxyInfo->TargetId) : nullptr;

            return  ctx->GetLibrary()->CreateProxy_TTD(handlerObj, targetObj);
        }

        void EmitAddtlInfo_SnapProxyInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapProxyInfo* proxyInfo = SnapObjectGetAddtlInfoAs<SnapProxyInfo*, SnapObjectType::SnapProxyObject>(snpObject);

            writer->WriteAddr(NSTokens::Key::handlerId, proxyInfo->HandlerId, NSTokens::Separator::CommaSeparator);
            writer->WriteAddr(NSTokens::Key::objectId, proxyInfo->TargetId, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapProxyInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapProxyInfo* proxyInfo = alloc.SlabAllocateStruct<SnapProxyInfo>();

            proxyInfo->HandlerId = reader->ReadAddr(NSTokens::Key::handlerId, true);
            proxyInfo->TargetId = reader->ReadAddr(NSTokens::Key::objectId, true);

            SnapObjectSetAddtlInfoAs<SnapProxyInfo*, SnapObjectType::SnapProxyObject>(snpObject, proxyInfo);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapProxyInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
            const SnapProxyInfo* proxyInfo1 = SnapObjectGetAddtlInfoAs<SnapProxyInfo*, SnapObjectType::SnapProxyObject>(sobj1);
            const SnapProxyInfo* proxyInfo2 = SnapObjectGetAddtlInfoAs<SnapProxyInfo*, SnapObjectType::SnapProxyObject>(sobj2);

            compareMap.CheckConsistentAndAddPtrIdMapping_Special(proxyInfo1->HandlerId, proxyInfo2->HandlerId, _u("handlerId"));
            compareMap.CheckConsistentAndAddPtrIdMapping_Special(proxyInfo1->TargetId, proxyInfo2->TargetId, _u("targetId"));
        }
#endif

        //////////////////

        Js::RecyclableObject* DoObjectInflation_SnapGeneratorInfo(const SnapObject* snpObject, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

            SnapGeneratorInfo* generatorInfo = SnapObjectGetAddtlInfoAs<SnapGeneratorInfo*, SnapObjectType::SnapGenerator>(snpObject);

            Field(Js::Var)* argVals = nullptr;
            if(generatorInfo->arguments_count > 0)
            {
                argVals = RecyclerNewArray(ctx->GetRecycler(), Field(Js::Var), generatorInfo->arguments_count);
                for(Js::RegSlot i = 0; i < generatorInfo->arguments_count; i++)
                {
                    argVals[i] = inflator->InflateTTDVar(generatorInfo->arguments_values[i]);
                }
            }

            Js::CallInfo callInfo(static_cast<Js::CallFlags>(generatorInfo->arguments_callInfo_flags), generatorInfo->arguments_callInfo_count);

            Js::Arguments arguments(callInfo, unsafe_write_barrier_cast<Js::Var*>(argVals));

            // TODO: BUGBUG - figure out how to determine what the prototype was.  Just use GetNull() for now
            Js::RecyclableObject* prototype = ctx->GetLibrary()->GetNull();
            //if (generatorInfo->generatorPrototype == 1) {
            //    prototype = ctx->GetLibrary()->GetNull();
            //}
            //else if (generatorInfo->generatorPrototype == 2) {
            //    prototype = ctx->GetLibrary()->CreateGeneratorConstructorPrototypeObject();
            //}
            //else {
            //    //TTDAssert(false, "unexpected prototype found JavascriptGenerator");
            //}

            //Js::DynamicType* type = reinterpret_cast<Js::DynamicType*>((snpObject->SnapType->TypePtrId != TTD_INVALID_PTR_ID) ? inflator->LookupType(snpObject->SnapType->TypePtrId): nullptr);

            return  ctx->GetLibrary()->CreateJavascriptGenerator_TTD(ctx, prototype, arguments, static_cast<Js::JavascriptGenerator::GeneratorState>(generatorInfo->state));
        }

        void DoAddtlValueInstantiation_SnapGeneratorInfo(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            Js::JavascriptGenerator* generator = reinterpret_cast<Js::JavascriptGenerator*>(obj);

            SnapGeneratorInfo* generatorInfo = SnapObjectGetAddtlInfoAs<SnapGeneratorInfo*, SnapObjectType::SnapGenerator>(snpObject);
            Js::ScriptFunction* scriptFunction = reinterpret_cast<Js::ScriptFunction*>((generatorInfo->scriptFunction != TTD_INVALID_PTR_ID) ? inflator->LookupObject(generatorInfo->scriptFunction) : nullptr);
            if (scriptFunction != nullptr)
            {
                generator->SetScriptFunction(scriptFunction);
                Js::FunctionBody* executeFunction = scriptFunction->GetFunctionBody();

#if ENABLE_PROFILE_INFO
                // call EnsureDynamicProfileInfo or we get asserts in some cases when creating the InterpreterStackFrame
                executeFunction->EnsureDynamicProfileInfo();
#endif
                bool doProfile = Js::InterpreterStackFrame::ShouldDoProfile(executeFunction);

                Js::InterpreterStackFrame* frame = Js::InterpreterStackFrame::CreateInterpreterStackFrameForGenerator(scriptFunction, executeFunction, generator, doProfile);
                TTDAssert(generator->GetFrame() == frame, "unexpected frame mis-match");

                Field(Js::Var)* frameSlotArray = nullptr;
                if (generatorInfo->frame_slotCount != 0)
                {
                    frameSlotArray = RecyclerNewArray(ctx->GetRecycler(), Field(Js::Var), generatorInfo->frame_slotCount);

                    for (Js::RegSlot i = 0; i < generatorInfo->frame_slotCount; i++)
                    {
                        if (generatorInfo->frame_slotArray[i] != nullptr)
                        {
                            frameSlotArray[i] = inflator->InflateTTDVar(generatorInfo->frame_slotArray[i]);
                        }
                        else
                        {
                            frameSlotArray[i] = nullptr;
                        }
                    }
                }

                generator->SetFrameSlots(generatorInfo->frame_slotCount, frameSlotArray);
                if (generatorInfo->byteCodeReader_offset > 0)
                {
                    frame->InitializeClosures();
                    frame->GetReader()->SetCurrentOffset(generatorInfo->byteCodeReader_offset);
                }
            }
            else
            {
                TTDAssert(generator->IsCompleted(), "Unexpected null scriptFunction when generator is not in completed state");
            }
        }

        void EmitAddtlInfo_SnapGeneratorInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapGeneratorInfo* sgi = SnapObjectGetAddtlInfoAs<SnapGeneratorInfo*, SnapObjectType::SnapGenerator>(snpObject);

            writer->WriteInt32(NSTokens::Key::i32Val, sgi->generatorPrototype, NSTokens::Separator::CommaSeparator);

            writer->WriteInt32(NSTokens::Key::i32Val, sgi->frame_slotCount, NSTokens::Separator::CommaSeparator);

            // frame_slotArray
            writer->WriteKey(NSTokens::Key::frame_slotArray, NSTokens::Separator::CommaAndBigSpaceSeparator);
            writer->WriteSequenceStart();
            for(Js::RegSlot i = 0; i < sgi->frame_slotCount; ++i)
            {
                NSSnapValues::EmitTTDVar(sgi->frame_slotArray[i], writer, i != 0 ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator);
            }
            writer->WriteSequenceEnd();

            writer->WriteUInt32(NSTokens::Key::u32Val, sgi->state, NSTokens::Separator::CommaSeparator);
            writer->WriteAddr(NSTokens::Key::objectId, sgi->scriptFunction, NSTokens::Separator::CommaSeparator);

            writer->WriteUInt32(NSTokens::Key::u32Val, sgi->arguments_callInfo_count, NSTokens::Separator::CommaSeparator);
            writer->WriteUInt32(NSTokens::Key::u32Val, sgi->arguments_callInfo_flags, NSTokens::Separator::CommaSeparator);
            writer->WriteUInt32(NSTokens::Key::u32Val, sgi->arguments_count, NSTokens::Separator::CommaSeparator);

            // arguments_values array
            writer->WriteKey(NSTokens::Key::arguments_values, NSTokens::Separator::CommaAndBigSpaceSeparator);
            writer->WriteSequenceStart();
            for(uint32 i = 0; i < sgi->arguments_count; ++i)
            {
                NSSnapValues::EmitTTDVar(sgi->arguments_values[i], writer, i != 0 ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator);
            }
            writer->WriteSequenceEnd();

            writer->WriteUInt32(NSTokens::Key::u32Val, sgi->byteCodeReader_offset, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapGeneratorInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapGeneratorInfo* sgi = alloc.SlabAllocateStruct<SnapGeneratorInfo>();

            sgi->generatorPrototype = reader->ReadInt32(NSTokens::Key::i32Val, true);

            sgi->frame_slotCount = reader->ReadInt32(NSTokens::Key::i32Val, true);

            if(sgi->frame_slotCount == 0)
            {
                sgi->frame_slotArray = nullptr;
            }
            else
            {
                sgi->frame_slotArray = alloc.SlabAllocateArray<TTDVar>(sgi->frame_slotCount);
            }
            reader->ReadKey(NSTokens::Key::frame_slotArray, true);
            reader->ReadSequenceStart();
            for(Js::RegSlot i = 0; i < sgi->frame_slotCount; ++i)
            {
                sgi->frame_slotArray[i] = NSSnapValues::ParseTTDVar(i != 0, reader);
            }
            reader->ReadSequenceEnd();


            sgi->state = reader->ReadUInt32(NSTokens::Key::u32Val, true);
            sgi->scriptFunction = reader->ReadAddr(NSTokens::Key::objectId, true);
            sgi->arguments_callInfo_count = reader->ReadUInt32(NSTokens::Key::u32Val, true);
            sgi->arguments_callInfo_flags = static_cast<uint8>(reader->ReadUInt32(NSTokens::Key::u32Val, true));
            sgi->arguments_count = reader->ReadUInt32(NSTokens::Key::u32Val, true);

            if(sgi->arguments_count == 0)
            {
                sgi->arguments_values = nullptr;
            }
            else
            {
                sgi->arguments_values = alloc.SlabAllocateArray<TTDVar>(sgi->arguments_count);
            }
            reader->ReadKey(NSTokens::Key::arguments_values, true);
            reader->ReadSequenceStart();
            for(uint32 i = 0; i < sgi->arguments_count; ++i)
            {
                sgi->arguments_values[i] = NSSnapValues::ParseTTDVar(i != 0, reader);
            }
            reader->ReadSequenceEnd();

            sgi->byteCodeReader_offset = reader->ReadUInt32(NSTokens::Key::u32Val, true);

            SnapObjectSetAddtlInfoAs<SnapGeneratorInfo*, SnapObjectType::SnapGenerator>(snpObject, sgi);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapGeneratorInfo(const SnapObject *sobj1, const SnapObject *sobj2, TTDCompareMap &compareMap)
        {
            // TODO
        }
#endif

        ////////////////////

        Js::RecyclableObject* DoObjectInflation_SnapGeneratorFunctionInfo(const SnapObject *snpObject, InflateMap *inflator)
        {
            Js::ScriptContext *ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapGeneratorFunctionInfo *sfi = SnapObjectGetAddtlInfoAs<SnapGeneratorFunctionInfo *, SnapObjectType::SnapGeneratorFunction>(snpObject);
            Js::JavascriptGeneratorFunction* func = ctx->GetLibrary()->CreateGeneratorFunction(Js::JavascriptGeneratorFunction::EntryGeneratorFunctionImplementation, sfi->isAnonymousFunction);
            return func;
        }

        void DoAddtlValueInstantiation_SnapGeneratorFunctionInfo(const SnapObject *snpObject, Js::RecyclableObject *obj, InflateMap *inflator)
        {
            Js::JavascriptGeneratorFunction *func = Js::JavascriptGeneratorFunction::FromVar(obj);
            SnapGeneratorFunctionInfo *sfi = SnapObjectGetAddtlInfoAs<SnapGeneratorFunctionInfo *, SnapObjectType::SnapGeneratorFunction>(snpObject);

            if(sfi->scriptFunction != TTD_INVALID_PTR_ID)
            {
                Js::GeneratorVirtualScriptFunction* gvsf = reinterpret_cast<Js::GeneratorVirtualScriptFunction*>(inflator->LookupObject(sfi->scriptFunction));
                func->SetScriptFunction(gvsf);
            }
        }

        void EmitAddtlInfo_SnapGeneratorFunctionInfo(const SnapObject *snpObject, FileWriter *writer)
        {
            SnapGeneratorFunctionInfo* sgfi = SnapObjectGetAddtlInfoAs<SnapGeneratorFunctionInfo*, SnapObjectType::SnapGeneratorFunction>(snpObject);
            writer->WriteAddr(NSTokens::Key::objectId, sgfi->scriptFunction, NSTokens::Separator::CommaSeparator);
            writer->WriteBool(NSTokens::Key::boolVal, sgfi->isAnonymousFunction, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapGeneratorFunctionInfo(SnapObject *snpObject, FileReader *reader, SlabAllocator &alloc)
        {
            SnapGeneratorFunctionInfo* sgfi = alloc.SlabAllocateStruct<SnapGeneratorFunctionInfo>();
            sgfi->scriptFunction = reader->ReadAddr(NSTokens::Key::objectId, true);
            sgfi->isAnonymousFunction = reader->ReadBool(NSTokens::Key::boolVal, true);
            SnapObjectSetAddtlInfoAs<SnapGeneratorFunctionInfo*, SnapObjectType::SnapGeneratorFunction>(snpObject, sgfi);
        }
#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapGeneratorFunctionInfo(const SnapObject *sobj1, const SnapObject *sobj2, TTDCompareMap &compareMap)
        {
            // TODO
        }
#endif
        ////////////////////

        Js::RecyclableObject* DoObjectInflation_SnapAsyncFunction(const SnapObject *snpObject, InflateMap *inflator)
        {
            Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapGeneratorFunctionInfo* info = SnapObjectGetAddtlInfoAs<SnapGeneratorFunctionInfo *, SnapObjectType::SnapAsyncFunction>(snpObject);
            Js::JavascriptAsyncFunction* func = ctx->GetLibrary()->CreateAsyncFunction(Js::JavascriptAsyncFunction::EntryAsyncFunctionImplementation, info->isAnonymousFunction);
            return func;
        }

        void DoAddtlValueInstantiation_SnapAsyncFunction(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        {
            Js::JavascriptAsyncFunction* func = Js::JavascriptAsyncFunction::FromVar(obj);
            SnapGeneratorFunctionInfo* info = SnapObjectGetAddtlInfoAs<SnapGeneratorFunctionInfo *, SnapObjectType::SnapAsyncFunction>(snpObject);

            if (info->scriptFunction != TTD_INVALID_PTR_ID)
            {
                Js::GeneratorVirtualScriptFunction* gvsf = reinterpret_cast<Js::GeneratorVirtualScriptFunction*>(inflator->LookupObject(info->scriptFunction));
                func->SetScriptFunction(gvsf);
            }
        }

        void EmitAddtlInfo_SnapAsyncFunction(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapGeneratorFunctionInfo* info = SnapObjectGetAddtlInfoAs<SnapGeneratorFunctionInfo*, SnapObjectType::SnapAsyncFunction>(snpObject);
            writer->WriteAddr(NSTokens::Key::objectId, info->scriptFunction, NSTokens::Separator::CommaSeparator);
            writer->WriteBool(NSTokens::Key::boolVal, info->isAnonymousFunction, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapAsyncFunction(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapGeneratorFunctionInfo* info = alloc.SlabAllocateStruct<SnapGeneratorFunctionInfo>();
            info->scriptFunction = reader->ReadAddr(NSTokens::Key::objectId, true);
            info->isAnonymousFunction = reader->ReadBool(NSTokens::Key::boolVal, true);
            SnapObjectSetAddtlInfoAs<SnapGeneratorFunctionInfo*, SnapObjectType::SnapAsyncFunction>(snpObject, info);
        }
#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapAsyncFunction(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
        }
#endif

        ////////////////////

        Js::RecyclableObject* DoObjectInflation_SnapGeneratorVirtualScriptFunctionInfo(const SnapObject *snpObject, InflateMap *inflator)
        {
            Js::ScriptContext *ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapGeneratorVirtualScriptFunctionInfo* sgvsf = SnapObjectGetAddtlInfoAs<SnapGeneratorVirtualScriptFunctionInfo *, SnapObjectType::SnapGeneratorVirtualScriptFunction>(snpObject);
            Js::FunctionBody* fbody = inflator->LookupFunctionBody(sgvsf->BodyRefId);
            return ctx->GetLibrary()->CreateGeneratorVirtualScriptFunction(fbody);
        }

        void DoAddtlValueInstantiation_SnapGeneratorVirtualScriptFunctionInfo(const SnapObject *snpObject, Js::RecyclableObject *obj, InflateMap *inflator)
        {
            Js::GeneratorVirtualScriptFunction* fobj = reinterpret_cast<Js::GeneratorVirtualScriptFunction*>(obj);
            SnapGeneratorVirtualScriptFunctionInfo *sgvsf = SnapObjectGetAddtlInfoAs<SnapGeneratorVirtualScriptFunctionInfo *, SnapObjectType::SnapGeneratorVirtualScriptFunction>(snpObject);

            // fill in all the details of the base class
            DoAddtlValueInstantiation_SnapScriptFunctionInfoEx(sgvsf, fobj, inflator);

            if(sgvsf->realFunction != TTD_INVALID_PTR_ID)
            {
                fobj->SetRealGeneratorFunction(reinterpret_cast<Js::JavascriptGeneratorFunction*>(inflator->LookupObject(sgvsf->realFunction)));
            }
        }

        void EmitAddtlInfo_SnapGeneratorVirtualScriptFunctionInfo(const SnapObject *snpObject, FileWriter *writer)
        {
            SnapGeneratorVirtualScriptFunctionInfo* sgvsf = SnapObjectGetAddtlInfoAs<SnapGeneratorVirtualScriptFunctionInfo*, SnapObjectType::SnapGeneratorVirtualScriptFunction>(snpObject);
            EmitAddtlInfo_SnapScriptFunctionInfoEx(sgvsf, writer);
            writer->WriteAddr(NSTokens::Key::objectId, sgvsf->realFunction, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapGeneratorVirtualScriptFunctionInfo(SnapObject *snpObject, FileReader *reader, SlabAllocator &alloc)
        {
            SnapGeneratorVirtualScriptFunctionInfo* sgvsf = alloc.SlabAllocateStruct<SnapGeneratorVirtualScriptFunctionInfo>();
            ParseAddtlInfo_SnapScriptFunctionInfoEx(sgvsf, reader, alloc);
            sgvsf->realFunction = reader->ReadAddr(NSTokens::Key::objectId, true);
            SnapObjectSetAddtlInfoAs<SnapGeneratorVirtualScriptFunctionInfo*, SnapObjectType::SnapGeneratorVirtualScriptFunction>(snpObject, sgvsf);
        }
#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapGeneratorVirtualScriptFunctionInfo(const SnapObject *sobj1, const SnapObject *sobj2, TTDCompareMap &compareMap)
        {
            // TODO
        }
#endif

        ////////////////////

        Js::RecyclableObject* DoObjectInflation_SnapJavascriptPromiseAsyncSpawnExecutorFunction(const SnapObject *snpObject, InflateMap *inflator)
        {
            Js::ScriptContext *ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapJavascriptPromiseAsyncSpawnExecutorFunctionInfo* info = SnapObjectGetAddtlInfoAs<SnapJavascriptPromiseAsyncSpawnExecutorFunctionInfo *, SnapObjectType::JavascriptPromiseAsyncSpawnExecutorFunction>(snpObject);
            Js::Var target = (info->target!= nullptr) ? inflator->InflateTTDVar(info->target) : nullptr;

            Js::JavascriptGenerator* generator = nullptr;
            if (info->generator != TTD_INVALID_PTR_ID)
            {
                generator = reinterpret_cast<Js::JavascriptGenerator*>(inflator->LookupObject(info->generator));
            }

            // TODO; why do we need to cast here??
            Js::RecyclableObject* res = reinterpret_cast<Js::RecyclableObject*>(ctx->GetLibrary()->CreatePromiseAsyncSpawnExecutorFunction(generator, target));
            return res;
        }

        void DoAddtlValueInstantiation_SnapJavascriptPromiseAsyncSpawnExecutorFunction(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        {
        }

        void EmitAddtlInfo_SnapJavascriptPromiseAsyncSpawnExecutorFunction(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapJavascriptPromiseAsyncSpawnExecutorFunctionInfo* info = SnapObjectGetAddtlInfoAs<SnapJavascriptPromiseAsyncSpawnExecutorFunctionInfo*, SnapObjectType::JavascriptPromiseAsyncSpawnExecutorFunction>(snpObject);
            writer->WriteAddr(NSTokens::Key::objectId, info->generator, NSTokens::Separator::CommaSeparator);
            writer->WriteKey(NSTokens::Key::target, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(info->target, writer, NSTokens::Separator::NoSeparator);
        }

        void ParseAddtlInfo_SnapJavascriptPromiseAsyncSpawnExecutorFunction(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapJavascriptPromiseAsyncSpawnExecutorFunctionInfo* info = alloc.SlabAllocateStruct<SnapJavascriptPromiseAsyncSpawnExecutorFunctionInfo>();
            info->generator = reader->ReadAddr(NSTokens::Key::objectId, true);
            reader->ReadKey(NSTokens::Key::target, true);
            info->target= NSSnapValues::ParseTTDVar(false, reader);
            SnapObjectSetAddtlInfoAs<SnapJavascriptPromiseAsyncSpawnExecutorFunctionInfo*, SnapObjectType::JavascriptPromiseAsyncSpawnExecutorFunction>(snpObject, info);
        }
#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapJavascriptPromiseAsyncSpawnExecutorFunction(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        {
        }
#endif

        ////////////////////

        Js::RecyclableObject *DoObjectInflation_SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo(const SnapObject *snpObject, InflateMap *inflator)
        {
            Js::ScriptContext *ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
            SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo* info = SnapObjectGetAddtlInfoAs<SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo *, SnapObjectType::JavascriptPromiseAsyncSpawnStepArgumentExecutorFunction>(snpObject);
            Js::Var reject = (info->reject != nullptr) ? inflator->InflateTTDVar(info->reject) : nullptr;
            Js::Var resolve = (info->resolve != nullptr) ? inflator->InflateTTDVar(info->resolve) : nullptr;
            Js::Var argument = (info->argument != nullptr) ? inflator->InflateTTDVar(info->argument) : nullptr;
            bool isReject = info->isReject;

            Js::JavascriptGenerator* generator = nullptr;
            if (info->generator != TTD_INVALID_PTR_ID)
            {
                generator = reinterpret_cast<Js::JavascriptGenerator*>(inflator->LookupObject(info->generator));
            }

            Js::JavascriptMethod entryPoint = nullptr;
            switch (info->entryPoint)
            {
            case 1:
                entryPoint = Js::JavascriptPromise::EntryJavascriptPromiseAsyncSpawnStepNextExecutorFunction;
                break;
            case 2:
                entryPoint = Js::JavascriptPromise::EntryJavascriptPromiseAsyncSpawnStepThrowExecutorFunction;
                break;
            case 3:
                entryPoint = Js::JavascriptPromise::EntryJavascriptPromiseAsyncSpawnCallStepExecutorFunction;
                break;
            default:
                TTDAssert(false, "Unexpected value for entryPoint when inflating JavascriptPromiseAsyncSpawnStepArgumentExecutorFunction");
                break;
            }

            return ctx->GetLibrary()->CreatePromiseAsyncSpawnStepArgumentExecutorFunction(entryPoint, generator, argument, resolve, reject, isReject);
        }

        void DoAddtlValueInstantiation_SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo(const SnapObject* snpObject, Js::RecyclableObject* obj, InflateMap* inflator)
        { }
        
        void EmitAddtlInfo_SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo(const SnapObject* snpObject, FileWriter* writer)
        {
            SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo* info = SnapObjectGetAddtlInfoAs<SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo*, SnapObjectType::JavascriptPromiseAsyncSpawnStepArgumentExecutorFunction>(snpObject);
            writer->WriteAddr(NSTokens::Key::objectId, info->generator, NSTokens::Separator::CommaSeparator);
            writer->WriteKey(NSTokens::Key::reject, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(info->reject, writer, NSTokens::Separator::NoSeparator);
            writer->WriteKey(NSTokens::Key::resolve, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(info->resolve, writer, NSTokens::Separator::NoSeparator);
            writer->WriteKey(NSTokens::Key::argument, NSTokens::Separator::CommaSeparator);
            NSSnapValues::EmitTTDVar(info->argument, writer, NSTokens::Separator::NoSeparator);
            writer->WriteUInt32(NSTokens::Key::u32Val, info->entryPoint, NSTokens::Separator::CommaSeparator);
            writer->WriteBool(NSTokens::Key::boolVal, info->isReject, NSTokens::Separator::CommaSeparator);
        }

        void ParseAddtlInfo_SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
        {
            SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo* info = alloc.SlabAllocateStruct<SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo>();
            info->generator = reader->ReadAddr(NSTokens::Key::objectId, true);
            reader->ReadKey(NSTokens::Key::reject, true);
            info->reject = NSSnapValues::ParseTTDVar(false, reader);
            reader->ReadKey(NSTokens::Key::resolve, true);
            info->resolve = NSSnapValues::ParseTTDVar(false, reader);
            reader->ReadKey(NSTokens::Key::argument, true);
            info->argument = NSSnapValues::ParseTTDVar(false, reader);
            info->entryPoint = reader->ReadUInt32(NSTokens::Key::u32Val, true);
            info->isReject = reader->ReadBool(NSTokens::Key::boolVal, true);
            SnapObjectSetAddtlInfoAs<SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo*, SnapObjectType::JavascriptPromiseAsyncSpawnStepArgumentExecutorFunction>(snpObject, info);
        }

#if ENABLE_SNAPSHOT_COMPARE
        void AssertSnapEquiv_SnapJavascriptPromiseAsyncSpawnStepArgumentExecutorFunctionInfo(const SnapObject* sobj1, const SnapObject* sobj2, TTDCompareMap& compareMap)
        { }
#endif
        //////////



    }
}

#endif
