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

#include "RegexCommon.h"
#include "RegexPattern.h"
#include "Library/RegexHelper.h"

#include "DataStructures/Option.h"
#include "DataStructures/ImmutableList.h"
#include "DataStructures/BufferBuilder.h"
#include "ByteCode/OpCodeUtilAsmJs.h"
#include "ByteCode/ByteCodeSerializer.h"

#include "Language/AsmJsModule.h"
#include "Library/ES5Array.h"

void ChakraBinaryBuildDateTimeHash(DWORD * buildDateHash, DWORD * buildTimeHash);

namespace Js
{
    const int magicConstant = *(int*)"ChBc";
    const int majorVersionConstant = 1;
    const int minorVersionConstant = 1;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
    // These magic constants can be enabled to bracket and check different sections of the serialization
    // file.  Turn on BYTE_CODE_MAGIC_CONSTANTS in ByteCodeSerializer.h to enable this.
    const int magicStartOfFunctionBody = *(int*)"fun[";
    const int magicEndOfFunctionBody = *(int*)"]fun";
    const int magicStartOfConstantTable = *(int*)"con[";
    const int magicEndOfConstantTable = *(int*)"]con";
    const int magicStartStringConstant = *(int*)"str[";
    const int magicEndStringConstant = *(int*)"]str";
    const int magicStartOfCacheIdToPropIdMap = *(int*)"cid[";
    const int magicEndOfCacheIdToPropIdMap = *(int*)"]cid";
    const int magicStartOfReferencedPropIdMap = *(int*)"rid[";
    const int magicEndOfReferencedPropIdMap = *(int*)"]rid";
    const int magicStartOfPropertyIdsForScopeSlotArray = *(int*)"scope[";
    const int magicEndOfPropertyIdsForScopeSlotArray = *(int*)"]scope";
    const int magicStartOfDebuggerScopes = *(int*)"dbgscope[";
    const int magicEndOfDebuggerScopes = *(int*)"]dbgscope";
    const int magicStartOfDebuggerScopeProperties = *(int*)"dbgscopeprop[";
    const int magicEndOfDebuggerScopeProperties = *(int*)"]dbgscopeprop";
    const int magicStartOfAux = *(int*)"aux[";
    const int magicEndOfAux = *(int*)"]aux";
    const int magicStartOfAuxVarArray = *(int*)"ava[";
    const int magicEndOfAuxVarArray = *(int*)"]ava";
    const int magicStartOfAuxIntArray = *(int*)"aia[";
    const int magicEndOfAuxIntArray = *(int*)"]aia";
    const int magicStartOfAuxFltArray = *(int*)"afa[";
    const int magicEndOfAuxFltArray = *(int*)"]afa";
    const int magicStartOfAuxPropIdArray = *(int*)"api[";
    const int magicEndOfAuxPropIdArray = *(int*)"]api";
    const int magicStartOfAuxFuncInfoArray = *(int*)"afi[";
    const int magicEndOfAuxFuncInfoArray = *(int*)"]afi";
    const int magicStartOfAsmJsFuncInfo = *(int*)"asmfuncinfo[";
    const int magicEndOfAsmJsFuncInfo = *(int*)"]asmfuncinfo";
    const int magicStartOfAsmJsModuleInfo = *(int*)"asmmodinfo[";
    const int magicEndOfAsmJsModuleInfo = *(int*)"]asmmodinfo";
#endif

    // Serialized files are architecture specific
#ifndef VALIDATE_SERIALIZED_BYTECODE
#if _M_AMD64
    const byte magicArchitecture = 64;
#else
    const byte magicArchitecture = 32;
#endif
#else
#if _M_AMD64
    const int magicArchitecture = *(int*)"amd";
#elif _M_IA64
    const int magicArchitecture = *(int*)"ia64";
#elif _M_ARM
    const int magicArchitecture = *(int*)"arm";
#elif _M_ARM_64
    const int magicArchitecture = *(int*)"arm64";
#else
    const int magicArchitecture = *(int*)"x86";
#endif
#endif

// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Byte Code Serializer Versioning scheme
// Version number is a GUID (128 bits). There are two versioning modes--Engineering and Release. Engineering mode is for day-to-day development. Every time chakra.dll is built a
// fresh new version is generated by hashing the build date and time. This means that a byte code file saved to disk is exactly tied to the binary that generated it. This works
// well for QA test runs and buddy tests because there is no chance of effects between runs.
//
// Release mode is used when chakra.dll is close to public release where there are actual changes to chakra. The GUID is a fixed number from build-to-build. This number will stay
// the same for releases where there is no change to chakra.dll. The reason for this is that we don't want to invalidate compatible byte code that has already been cached.
enum FileVersionScheme : byte
{
    // Currently Chakra and ChakraCore versioning scheme is different.
    // Same version number for Chakra and ChakraCore doesn't mean they are the same.
    // Give the versioning scheme different value, so that byte code generate from one won't be use in the other.
#ifdef NTBUILD
    EngineeringVersioningScheme = 10,
    ReleaseVersioningScheme = 20,
#else
    EngineeringVersioningScheme = 11,
    ReleaseVersioningScheme = 21,
#endif

#if (defined(NTBUILD) && CHAKRA_VERSION_RELEASE) || (!defined(NTBUILD) && CHAKRA_CORE_VERSION_RELEASE)
    CurrentFileVersionScheme = ReleaseVersioningScheme
#else
    CurrentFileVersionScheme = EngineeringVersioningScheme
#endif
};


// it should be in separate file for testing
#include "ByteCodeCacheReleaseFileVersion.h"

// Used for selective serialization of Function Body fields to make the representation compact
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) bool has_##name : 1
#define DECLARE_MANUAL_SERIALIZABLE_FIELD(type, name, serializableType, deserializeHere) bool has_##name: 1

#define DEFINE_ALL_FIELDS
struct SerializedFieldList {
#include "SerializableFunctionFields.h"
    bool has_m_lineNumber: 1;
    bool has_m_columnNumber: 1;
};

C_ASSERT(sizeof(GUID)==sizeof(DWORD)*4);
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// Holds a buffer and size for use by the serializer
struct ByteBuffer
{
    uint32 byteCount;
    union
    {
        void * pv;
        const wchar_t * s16;
        const char * s8;
    };
public:
    ByteBuffer(uint32 byteCount, void * pv)
        : byteCount(byteCount), pv(pv)
    { }
};

template<>
struct DefaultComparer<ByteBuffer*>
{
    static bool Equals(ByteBuffer const * str1, ByteBuffer const * str2)
    {
        if (str1->byteCount != str2->byteCount)
        {
            return false;
        }
        return memcmp(str1->pv, str2->pv, str1->byteCount)==0;
    }

    static hash_t GetHashCode(ByteBuffer const * str)
    {
        return JsUtil::CharacterBuffer<char>::StaticGetHashCode(str->s8, str->byteCount);
    }
};

struct IndexEntry
{
    BufferBuilderByte* isPropertyRecord;
    int id;
};


#pragma pack(push, 1)
struct StringIndexRecord
{
    int offset;
    bool isPropertyRecord;
};
#pragma pack(pop)

typedef JsUtil::BaseDictionary<ByteBuffer*, IndexEntry, ArenaAllocator, PrimeSizePolicy, DefaultComparer> TString16ToId;

// Boolean flags on the FunctionBody
enum FunctionFlags
{
    ffIsDeclaration                    = 0x0001,
    ffHasImplicitArgsIn                = 0x0002,
    ffIsAccessor                       = 0x0004,
    ffIsGlobalFunc                     = 0x0008,
    ffDontInline                       = 0x0010,
    ffIsFuncRegistered                 = 0x0020,
    ffIsStaticNameFunction             = 0x0040,
    ffIsStrictMode                     = 0x0080,
    ffDoBackendArgumentsOptimization   = 0x0100,
    ffIsEval                           = 0x0200,
    ffIsDynamicFunction                = 0x0400,
    ffhasAllNonLocalReferenced         = 0x0800,
    ffhasSetIsObject                   = 0x1000,
    ffhasSetCallsEval                  = 0x2000,
    ffIsNameIdentifierRef              = 0x4000,
    ffChildCallsEval                   = 0x8000,
    ffHasReferenceableBuiltInArguments = 0x10000,
    ffIsNamedFunctionExpression        = 0x20000,
    ffIsAsmJsMode                      = 0x40000,
    ffIsAsmJsFunction                  = 0x80000
};

// Kinds of constant
enum ConstantType : byte
{
    ctInt = 1,
    ctString16 = 2,
    ctNull = 3,
    ctUndefined = 4,
    ctNumber = 5,
    ctNullDisplay = 6,
    ctStrictNullDisplay = 7,
    ctTrue = 8,
    ctFalse = 9,
    ctStringTemplateCallsite = 10
};


// Try to convert from size_t to uint32. May overflow (and return false) on 64-bit.
bool TryConvertToUInt32(size_t size, uint32 * out)
{
    *out = (uint32)size;
    if (sizeof(size) == sizeof(uint32))
    {
        return true;
    }
    Assert(sizeof(size_t) == sizeof(uint64));
    if((uint64)(*out) == size)
    {
        return true;
    }
    AssertMsg(false, "Is it really an offset greater than 32 bits?"); // More likely a bug somewhere.
    return false;
}

#if VARIABLE_INT_ENCODING
template <typename T>
static const byte * ReadVariableInt(const byte * buffer, size_t remainingBytes, T * value)
{
    Assert(remainingBytes >= sizeof(byte));
    byte firstByte = *(byte*) buffer;

    if (firstByte >= MIN_SENTINEL)
    {
        Assert(remainingBytes >= sizeof(uint16));
        const byte* locationOfValue = buffer + 1;

        if (firstByte == TWO_BYTE_SENTINEL)
        {
            uint16 twoByteValue = *((serialization_alignment uint16*) locationOfValue);
            Assert(twoByteValue > ONE_BYTE_MAX);

            *value = twoByteValue;
            PHASE_PRINT_TESTTRACE1(Js::VariableIntEncodingPhase, L"TestTrace: VariableIntEncoding (decode)- 2 bytes, value %u\n", *value);
            return buffer + sizeof(uint16) +SENTINEL_BYTE_COUNT;
        }
        else
        {
            Assert(remainingBytes >= sizeof(T));
            Assert(firstByte == FOUR_BYTE_SENTINEL);
            *value = *((serialization_alignment T*) locationOfValue);
            Assert(*value > TWO_BYTE_MAX || *value <= 0);

            PHASE_PRINT_TESTTRACE1(Js::VariableIntEncodingPhase, L"TestTrace: VariableIntEncoding (decode) - 4 bytes, value %u\n", *value);
            return buffer + sizeof(T) +SENTINEL_BYTE_COUNT;
        }
    }
    else
    {
        *value = (T) firstByte;
        PHASE_PRINT_TESTTRACE1(Js::VariableIntEncodingPhase, L"TestTrace: VariableIntEncoding (decode) - 1 byte, value %u\n", *value);
        return buffer + sizeof(byte);
    }
}
#endif

// Compile-time-check some invariants that the file format depends on
C_ASSERT(sizeof(PropertyId)==sizeof(int32));


// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//  Byte Code File Header Layout
//  Offset      Size    Name                                    Value
//  0       4       Magic Number                                "ChBc"
//  4       4       Total File Size
//  8       1       File Version Scheme                     10 for engineering      20 for release
//  9       4       Version DWORD 1                             jscript minor version   GUID quad part 1
//  13      4       Version DWORD 2                             jscript major version   GUID quad part 2
//  17      4       Version DWORD 3                             hash of __DATE__            GUID quad part 3
//  21      4       Version DWORD 4                             hash of __TIME__            GUID quad part 4
//  25      4       Expected Architecture                       "amd"0, "ia64", "arm"0 or "x86"0
//  29      4       Expected Function Body Size
//  33      4       Expected Built In PropertyCount
//  37      4       Expected Op Code Count
//  41      4       Size of Original Source Code
//  45      4       Count of Auxiliary Structures
//  49      4       Smallest Literal Object ID
//  53      4       Largest Literal Object ID
//  57      4       Offset from start of this file
//                    to Strings Table
//  61      4       Offset to Source Spans
//  65      4       Count of Functions
//  69      4       Offset to Functions
//  73      4       Offset to Auxiliary Structures
//  77      4       Count of Strings
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// This is the serializer
class ByteCodeBufferBuilder
{
    // Begin File Layout -------------------------------
    ConstantSizedBufferBuilderOf<int32> magic;
    ConstantSizedBufferBuilderOf<int32> totalSize;  // The size is unknown when the offsets are calculated so just reserve 4 bytes for this for now to avoid doing two passes to calculate the offsets
    BufferBuilderByte fileVersionKind; // Engineering or Release
    ConstantSizedBufferBuilderOf<int32> V1; // V1-V4 are the parts of the version. It is a fixed version GUID or a per-build version.
    ConstantSizedBufferBuilderOf<int32> V2;
    ConstantSizedBufferBuilderOf<int32> V3;
    ConstantSizedBufferBuilderOf<int32> V4;
    BufferBuilderInt32 architecture;
    BufferBuilderInt32 expectedFunctionBodySize;
    BufferBuilderInt32 expectedBuildInPropertyCount;
    BufferBuilderInt32 expectedOpCodeCount;
    BufferBuilderInt32 originalSourceSize;
    BufferBuilderInt32 originalCharLength;
    BufferBuilderRelativeOffset string16sOffset;
    BufferBuilderRelativeOffset sourceSpansOffset;
    BufferBuilderRelativeOffset lineInfoCacheOffset;
    BufferBuilderRelativeOffset functionsOffset;
    BufferBuilderInt32 string16Count;
    BufferBuilderList string16IndexTable;
    BufferBuilderList string16Table;
    BufferBuilderAligned alignedString16Table;
    BufferBuilderList sourceSpans;
    BufferBuilderInt32 lineInfoCacheCount;
    BufferBuilderRaw lineInfoCache;
    BufferBuilderInt32 functionCount;
    BufferBuilderList functionsTable;
    // End File Layout ---------------------------------
    ArenaAllocator * alloc;
    TString16ToId * string16ToId;
    int nextString16Id;
    int topFunctionId;
    LPCUTF8 utf8Source;
    ScriptContext * scriptContext;
    BufferBuilder * startOfCachedScopeAuxBlock;
    DWORD dwFlags;
    DWORD dwFunctionTableLength;
    BYTE *functionTable;

    //Instead of referencing TotalNumberOfBuiltInProperties directly; or PropertyIds::_countJSOnlyProperty we use this.
    //For library code this will be set to _countJSOnlyProperty and for normal bytecode this will be TotalNumberOfBuiltInProperties
    int builtInPropertyCount;

    bool GenerateLibraryByteCode() const
    {
        return (dwFlags & GENERATE_BYTE_CODE_BUFFER_LIBRARY) != 0;
    }

    bool OmitFunction(int serializationIndex) const
    {
        return functionTable && ((serializationIndex >= (int)dwFunctionTableLength) || !functionTable[serializationIndex]);
    }

    bool GenerateByteCodeForNative(int serializationIndex) const
    {
        return (dwFlags & GENERATE_BYTE_CODE_FOR_NATIVE) != 0;
    }

public:

    ByteCodeBufferBuilder(uint32 sourceSize, uint32 sourceCharLength, LPCUTF8 utf8Source, DWORD dwFunctionTableLength, BYTE * functionTable, Utf8SourceInfo* sourceInfo, ScriptContext * scriptContext, ArenaAllocator * alloc, DWORD dwFlags, int builtInPropertyCount)
        : magic(L"Magic", magicConstant),
          totalSize(L"Total Size", 0),
          fileVersionKind(L"FileVersionKind", 0),
          V1(L"V1", 0),
          V2(L"V2", 0),
          V3(L"V3", 0),
          V4(L"V4", 0),
          architecture(L"Expected Architecture", magicArchitecture),
          expectedFunctionBodySize(L"Expected Function Body Size", sizeof(unaligned FunctionBody)),
          expectedBuildInPropertyCount(L"Expected Built-in Properties", builtInPropertyCount),
          expectedOpCodeCount(L"Expected Number of OpCodes",  (int)OpCode::Count),
          originalSourceSize(L"Source Size", sourceSize),
          originalCharLength(L"Source Char Length", sourceCharLength),
          string16sOffset(L"Offset of String16s", &string16Count),
          sourceSpansOffset(L"Offset of Source Spans", &sourceSpans),
          lineInfoCacheOffset(L"Offset of Source Spans", &lineInfoCacheCount),
          functionCount(L"Function Count", 0),
          functionsOffset(L"Offset of Functions", &functionCount),
          string16Count(L"String16 Count", 0),
          string16IndexTable(L"String16 Indexes"),
          string16Table(L"String16 Table"),
          alignedString16Table(L"Alignment for String16 Table", &string16Table, sizeof(wchar_t)),
          sourceSpans(L"Source Spans"),
          lineInfoCacheCount(L"Line Info Cache", sourceInfo->GetLineOffsetCache()->GetLineCount()),
          lineInfoCache(L"Line Info Cache", lineInfoCacheCount.value * sizeof(JsUtil::LineOffsetCache<Recycler>::LineOffsetCacheItem), (byte *)sourceInfo->GetLineOffsetCache()->GetItems()),
          functionsTable(L"Functions"),
          nextString16Id(builtInPropertyCount), // Reserve the built-in property ids
          topFunctionId(0),
          utf8Source(utf8Source),
          scriptContext(scriptContext),
          startOfCachedScopeAuxBlock(nullptr),
          alloc(alloc),
          dwFlags(dwFlags),
          builtInPropertyCount(builtInPropertyCount),
          dwFunctionTableLength(dwFunctionTableLength),
          functionTable(functionTable)
    {
        if (GenerateLibraryByteCode())
        {
            expectedFunctionBodySize.value = 0;
            expectedOpCodeCount.value = 0;
        }

        // library alaways use the release versioning scheme
        byte actualFileVersionScheme = GenerateLibraryByteCode()? ReleaseVersioningScheme : CurrentFileVersionScheme;
#if ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema)
        {
            actualFileVersionScheme = (byte)Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema;
        }
#endif
        fileVersionKind.value = actualFileVersionScheme;
        if (actualFileVersionScheme != ReleaseVersioningScheme)
        {
            Assert(!GenerateLibraryByteCode());
            Assert(actualFileVersionScheme == EngineeringVersioningScheme);
            DWORD jscriptMajor, jscriptMinor, buildDateHash, buildTimeHash;
            Js::VerifyOkCatastrophic(AutoSystemInfo::GetJscriptFileVersion(&jscriptMajor, &jscriptMinor, &buildDateHash, &buildTimeHash));
            V1.value = jscriptMajor;
            V2.value = jscriptMinor;
            V3.value = buildDateHash;
            V4.value = buildTimeHash;
        }
        else
        {
            auto guidDWORDs = (DWORD*)(&byteCodeCacheReleaseFileVersion);
            V1.value = guidDWORDs[0];
            V2.value = guidDWORDs[1];
            V3.value = guidDWORDs[2];
            V4.value = guidDWORDs[3];
        }
#if ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.ForceSerializedBytecodeMajorVersion)
        {
            V1.value = Js::Configuration::Global.flags.ForceSerializedBytecodeMajorVersion;
            V2.value = 0;
            V3.value = 0;
            V4.value = 0;
        }
#endif
        string16ToId = Anew(alloc, TString16ToId, alloc);
    }

    HRESULT Create(bool allocateBuffer, byte ** buffer, DWORD * bufferBytes)
    {
        BufferBuilderList all(L"Final");

        // Reverse the lists
        string16IndexTable.list = string16IndexTable.list->ReverseCurrentList();
        string16Table.list = string16Table.list->ReverseCurrentList();

        // Prepend all sections (in reverse order because of prepend)
        all.list = all.list->Prepend(&functionsTable, alloc);
        all.list = all.list->Prepend(&functionCount, alloc);
        all.list = all.list->Prepend(&lineInfoCache, alloc);
        all.list = all.list->Prepend(&lineInfoCacheCount, alloc);
        all.list = all.list->Prepend(&alignedString16Table, alloc);
        all.list = all.list->Prepend(&string16IndexTable, alloc);
        all.list = all.list->Prepend(&string16Count, alloc);
        all.list = all.list->Prepend(&functionsOffset, alloc);
        all.list = all.list->Prepend(&sourceSpansOffset, alloc);
        all.list = all.list->Prepend(&lineInfoCacheOffset, alloc);
        all.list = all.list->Prepend(&string16sOffset, alloc);
        all.list = all.list->Prepend(&originalCharLength, alloc);
        all.list = all.list->Prepend(&originalSourceSize, alloc);
        all.list = all.list->Prepend(&expectedOpCodeCount, alloc);
        all.list = all.list->Prepend(&expectedBuildInPropertyCount, alloc);
        all.list = all.list->Prepend(&expectedFunctionBodySize, alloc);
        all.list = all.list->Prepend(&architecture, alloc);
        all.list = all.list->Prepend(&V4, alloc);
        all.list = all.list->Prepend(&V3, alloc);
        all.list = all.list->Prepend(&V2, alloc);
        all.list = all.list->Prepend(&V1, alloc);
        all.list = all.list->Prepend(&fileVersionKind, alloc);
        all.list = all.list->Prepend(&totalSize, alloc);
        all.list = all.list->Prepend(&magic, alloc);

        // Get the string count.
        string16Count.value = nextString16Id - this->builtInPropertyCount;

        // Figure out the size and set all individual offsets
        DWORD size = all.FixOffset(0);
        totalSize.value = size;

        // Allocate the bytes
        if (allocateBuffer)
        {
            *bufferBytes = size;
            *buffer = (byte*)CoTaskMemAlloc(*bufferBytes);
            if (*buffer == nullptr)
            {
                return E_OUTOFMEMORY;
            }
        }

        if (size > *bufferBytes)
        {
            *bufferBytes = size;
            return *buffer == nullptr ? S_OK : E_INVALIDARG;
        }
        else
        {
            // Write into the buffer
            all.Write(*buffer, *bufferBytes);
            *bufferBytes = size;
            DebugOnly(Output::Flush());         // Flush trace
            return S_OK;
        }
    }

    bool isBuiltinProperty(PropertyId pid) {
        if (pid < this->builtInPropertyCount || pid==/*nil*/0xffffffff)
        {
            return true;
        }
        return false;
    };

    PropertyId encodeNonBuiltinPropertyId(PropertyId id) {
        const PropertyRecord * propertyValue = nullptr;
        Assert(id >= this->builtInPropertyCount); // Shouldn't have gotten a builtin property id
        propertyValue = scriptContext->GetPropertyName(id);
        id = GetIdOfPropertyRecord(propertyValue) - this->builtInPropertyCount;
        return id ^ SERIALIZER_OBSCURE_NONBUILTIN_PROPERTY_ID;
    };

    PropertyId encodePossiblyBuiltInPropertyId(PropertyId id) {
        const PropertyRecord * propertyValue = nullptr;
        if(id >= this->builtInPropertyCount)
        {
            propertyValue = scriptContext->GetPropertyName(id);
            id = GetIdOfPropertyRecord(propertyValue);
        }
        return id ^ SERIALIZER_OBSCURE_PROPERTY_ID;
    };

    int GetString16Id(ByteBuffer * bb, bool isPropertyRecord = false)
    {
        IndexEntry indexEntry;
        if (!string16ToId->TryGetValue(bb, &indexEntry))
        {
            auto sizeInBytes = bb->byteCount;
            auto stringEntry = Anew(alloc, BufferBuilderRaw, L"String16", sizeInBytes, (const byte *)bb->pv); // Writing the terminator even though it is computable so that this memory can be used as-is when deserialized
            string16Table.list = string16Table.list->Prepend(stringEntry, alloc);
            if (string16IndexTable.list == nullptr)
            {
                // First item in the list is the first string.
                auto stringIndexEntry = Anew(alloc, BufferBuilderRelativeOffset, L"First String16 Index", stringEntry);
                string16IndexTable.list = string16IndexTable.list->Prepend(stringIndexEntry, alloc);
                PrependByte(string16IndexTable, L"isPropertyRecord", (BYTE)isPropertyRecord);
            }

            // Get a pointer to the previous entry of isPropertyRecord
            indexEntry.isPropertyRecord = static_cast<BufferBuilderByte*>(string16IndexTable.list->First());

            // Subsequent strings indexes point one past the end. This way, the size is always computable by subtracting indexes.
            auto stringIndexEntry = Anew(alloc, BufferBuilderRelativeOffset, L"String16 Index", stringEntry, sizeInBytes);
            string16IndexTable.list = string16IndexTable.list->Prepend(stringIndexEntry, alloc);

            // By default, mark the next string to be not a property record.
            PrependByte(string16IndexTable, L"isPropertyRecord", (BYTE)false);

            indexEntry.id = nextString16Id;
            string16ToId->Add(bb, indexEntry);
            ++nextString16Id;
        }
        // A string might start off as not being a property record and later becoming one. Hence,
        // we set only if the transition is from false => true. Once it is a property record, it cannot go back.
        if(isPropertyRecord)
        {
            indexEntry.isPropertyRecord->value = isPropertyRecord;
        }
        return indexEntry.id;
    }

    uint32 PrependRelativeOffset(BufferBuilderList & builder, LPCWSTR clue, BufferBuilder * pointedTo)
    {
        auto entry = Anew(alloc, BufferBuilderRelativeOffset, clue, pointedTo, 0);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(int32);
    }

    uint32 PrependInt16(BufferBuilderList & builder, LPCWSTR clue, int16 value, BufferBuilderInt16 ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, BufferBuilderInt16, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int16);
    }

    uint32 PrependInt32(BufferBuilderList & builder, LPCWSTR clue, int value, BufferBuilderInt32 ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, BufferBuilderInt32, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int32);
    }

    uint32 PrependConstantInt16(BufferBuilderList & builder, LPCWSTR clue, int16 value, ConstantSizedBufferBuilderOf<int16> ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<int16>, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int16);
    }

    uint32 PrependConstantInt32(BufferBuilderList & builder, LPCWSTR clue, int value, ConstantSizedBufferBuilderOf<int> ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<int>, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int32);
    }

    uint32 PrependConstantInt64(BufferBuilderList & builder, LPCWSTR clue, int64 value, ConstantSizedBufferBuilderOf<int64> ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<int64>, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int64);
    }

    uint32 PrependByte(BufferBuilderList & builder, LPCWSTR clue, byte value)
    {
        auto entry = Anew(alloc, BufferBuilderByte, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(byte);
    }

    uint32 PrependFunctionBodyFlags(BufferBuilderList & builder, LPCWSTR clue, FunctionBody::FunctionBodyFlags value)
    {
        return PrependByte(builder, clue, (byte) value);
    }

    uint32 PrependBool(BufferBuilderList & builder, LPCWSTR clue, bool value)
    {
        return PrependByte(builder, clue, (byte) value);
    }

    uint32 PrependFloat(BufferBuilderList & builder, LPCWSTR clue, float value)
    {
        auto entry = Anew(alloc, BufferBuilderFloat, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(float);
    }

    uint32 PrependDouble(BufferBuilderList & builder, LPCWSTR clue, double value)
    {
        auto entry = Anew(alloc, BufferBuilderDouble, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(double);
    }

    uint32 PrependSIMDValue(BufferBuilderList & builder, LPCWSTR clue, SIMDValue value)
    {
        auto entry = Anew(alloc, BufferBuilderSIMD, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(SIMDValue);
    }

    uint32 PrependString16(__in BufferBuilderList & builder, __in_nz LPCWSTR clue, __in_bcount_opt(byteLength) LPCWSTR sz, __in uint32 byteLength)
    {
        if (sz != nullptr)
        {
            auto bb = Anew(alloc, ByteBuffer, byteLength, (void*)sz); // Includes trailing null
            return PrependInt32(builder, clue, GetString16Id(bb));
        }
        else
        {
            return PrependInt32(builder, clue, 0xffffffff);
        }
    }

    uint32 PrependByteBuffer(BufferBuilderList & builder, LPCWSTR clue, ByteBuffer * bb)
    {
        auto id = GetString16Id(bb);
        return PrependInt32(builder, clue, id);
    }

    int GetIdOfPropertyRecord(const PropertyRecord * propertyRecord)
    {
        AssertMsg(!propertyRecord->IsSymbol(), "bytecode serializer does not currently handle non-built-in symbol PropertyRecords");
        size_t byteCount = ((size_t)propertyRecord->GetLength() + 1) * sizeof(wchar_t);
        if (byteCount > UINT_MAX)
        {
            // We should never see property record that big
            Js::Throw::InternalError();
        }
        auto buffer = propertyRecord->GetBuffer();
#if DBG
        const PropertyRecord * propertyRecordCheck;
        scriptContext->FindPropertyRecord(buffer, propertyRecord->GetLength(), &propertyRecordCheck);
        Assert(propertyRecordCheck == propertyRecord);
#endif
        auto bb = Anew(alloc, ByteBuffer, (uint32)byteCount, (void*)buffer);
        return GetString16Id(bb, /*isPropertyRecord=*/ true);
    }

    template<typename TLayout>
    unaligned TLayout * DuplicateLayout(unaligned const TLayout * in)
    {
        auto sizeOfLayout = sizeof(unaligned TLayout);
        auto newLayout = AnewArray(alloc, byte, sizeOfLayout);
        js_memcpy_s(newLayout, sizeOfLayout, in, sizeOfLayout);
        return (unaligned TLayout * )newLayout;
    }

    template<typename T>
    uint32 Prepend(BufferBuilderList & builder, LPCWSTR clue, T * t)
    {
        auto block = Anew(alloc, BufferBuilderRaw, clue, sizeof(serialization_alignment T), (const byte*)t);
        builder.list = builder.list->Prepend(block, alloc);
        return sizeof(serialization_alignment T);
    }

    struct AuxRecord
    {
        SerializedAuxiliaryKind kind;
        uint offset;
    };

#ifndef TEMP_DISABLE_ASMJS
    HRESULT RewriteAsmJsByteCodesInto(BufferBuilderList & builder, LPCWSTR clue, FunctionBody * function, ByteBlock * byteBlock)
    {
        SListCounted<AuxRecord> auxRecords(alloc);

        Assert(!OmitFunction(function->GetSerializationIndex()));

        auto finalSize = Anew(alloc, BufferBuilderInt32, L"Final Byte Code Size", 0); // Initially set to zero
        builder.list = builder.list->Prepend(finalSize, alloc);

        ByteCodeReader reader;
        reader.Create(function);

        uint32 size = 0;
        const byte * opStart = nullptr;
        bool cantGenerate = false;

        auto saveBlock = [&]() {
            uint32 byteCount;
            if (TryConvertToUInt32(reader.GetIP() - opStart, &byteCount))
            {
                if (!GenerateByteCodeForNative(function->GetSerializationIndex()))
                {
                    auto block = Anew(alloc, BufferBuilderRaw, clue, byteCount, (const byte*)opStart);
                    builder.list = builder.list->Prepend(block, alloc);
                    size += byteCount;
                }
            }
            else
            {
                AssertMsg(false, "Unlikely: byte code size overflows 32 bits");
                cantGenerate = true;
            }
        };

        Assert(!function->HasCachedScopePropIds());

        while (!cantGenerate)
        {
            opStart = reader.GetIP();
            opStart; // For prefast. It can't figure out that opStart is captured in saveBlock above.
            LayoutSize layoutSize;
            OpCodeAsmJs op = (OpCodeAsmJs)reader.ReadOp(layoutSize);
            if (op == OpCodeAsmJs::EndOfBlock)
            {
                saveBlock();
                break;
            }

            OpLayoutTypeAsmJs layoutType = OpCodeUtilAsmJs::GetOpCodeLayout(op);
            switch (layoutType)
            {

#define DEFAULT_LAYOUT(op) \
    case OpLayoutTypeAsmJs::##op: { \
        Assert(layoutSize == SmallLayout); \
        reader.##op(); \
        saveBlock(); \
        break; }
#define DEFAULT_LAYOUT_WITH_ONEBYTE(op) \
    case OpLayoutTypeAsmJs::##op: { \
        switch (layoutSize) \
        { \
        case SmallLayout: \
            reader.##op##_Small(); \
            break; \
        case MediumLayout: \
            reader.##op##_Medium(); \
            break; \
        case LargeLayout: \
            reader.##op##_Large(); \
            break; \
        default: \
            Assume(UNREACHED); \
        } \
       saveBlock(); \
       break;     }

                DEFAULT_LAYOUT(Empty);
                DEFAULT_LAYOUT(StartCall);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementSlot);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmTypedArr);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmCall);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmReg1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmReg2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmReg3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmReg4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmReg5);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmReg6);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmReg7);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmReg2IntConst1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Double1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Float1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Double1Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Double1Float1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Double1Reg1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float1Reg1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Reg1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg1Double1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg1Float1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg1Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Double2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Float2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Const1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Double2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float1Double1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float1Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Double3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmUnsigned1);
                DEFAULT_LAYOUT(AsmBr);
                DEFAULT_LAYOUT_WITH_ONEBYTE(BrInt1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(BrInt2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_1Float4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_2Int4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_3Int4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_1Float1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_2Float1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_1Float64x2_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_1Int32x4_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_1Int32x4_1Float32x4_2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg1Float32x4_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Float32x4_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_1Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_1Int4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_2Int4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_3Int4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_2Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_2Int2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Int32x4_1Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_2Int1Float1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float1Float32x4_1Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg1Int32x4_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_1Float64x2_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int32x4_1Float32x4_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Int32x4_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_1Double2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_1Double1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_2Double1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_2Int2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_3Int2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_1Float32x4_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_1Int32x4_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Float64x2_1Int32x4_1Float64x2_2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg1Float64x2_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Int1Float64x2_1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(AsmSimdTypedArr);

#undef DEFAULT_LAYOUT
#undef DEFAULT_LAYOUT_WITH_ONEBYTE
            default:
                AssertMsg(false, "Unknown OpLayout");
                cantGenerate = true;
                break;
            }
        }

        if (cantGenerate)
        {
            return ByteCodeSerializer::CantGenerate;
        }

        if (size != byteBlock->GetLength() && !GenerateByteCodeForNative(function->GetSerializationIndex()))
        {
            Assert(size == byteBlock->GetLength());
            return ByteCodeSerializer::CantGenerate;
        }
        finalSize->value = size;

        RewriteAuxiliaryInto(builder, auxRecords, reader, function);
        return S_OK;
    }
#endif

    HRESULT RewriteByteCodesInto(BufferBuilderList & builder, LPCWSTR clue, FunctionBody * function, ByteBlock * byteBlock)
    {
        SListCounted<AuxRecord> auxRecords(alloc);

        Assert(!OmitFunction(function->GetSerializationIndex()));

        auto finalSize = Anew(alloc, BufferBuilderInt32, L"Final Byte Code Size", 0); // Initially set to zero
        builder.list = builder.list->Prepend(finalSize, alloc);

        ByteCodeReader reader;
        reader.Create(function);

        uint32 size = 0;
        const byte * opStart = nullptr;
        bool cantGenerate = false;

        auto saveBlock = [&]() {
            uint32 byteCount;
            if (TryConvertToUInt32(reader.GetIP()-opStart, &byteCount))
            {
                if (!GenerateByteCodeForNative(function->GetSerializationIndex()))
                {
                    auto block = Anew(alloc, BufferBuilderRaw, clue, byteCount, (const byte*) opStart);
                    builder.list = builder.list->Prepend(block, alloc);
                    size += byteCount;
                }
            }
            else
            {
                AssertMsg(false, "Unlikely: byte code size overflows 32 bits");
                cantGenerate = true;
            }
        };

        if (function->HasCachedScopePropIds())
        {
            AuxRecord record = { sakPropertyIdArrayForCachedScope, 0 };
            auxRecords.Prepend(record);
        }

        while(!cantGenerate)
        {
            opStart = reader.GetIP();
            opStart; // For prefast. It can't figure out that opStart is captured in saveBlock above.
            LayoutSize layoutSize;
            OpCode op = reader.ReadOp(layoutSize);
            if (op == OpCode::EndOfBlock)
            {
                saveBlock();
                break;
            }

            OpLayoutType layoutType = OpCodeUtil::GetOpCodeLayout(op);
            switch (layoutType)
            {

#define DEFAULT_LAYOUT(op) \
    case OpLayoutType::##op: { \
        Assert(layoutSize == SmallLayout); \
        reader.##op(); \
        saveBlock(); \
        break; }
#define DEFAULT_LAYOUT_WITH_ONEBYTE(op) \
    case OpLayoutType::##op: { \
        switch (layoutSize) \
        { \
        case SmallLayout: \
            reader.##op##_Small(); \
            break; \
        case MediumLayout: \
            reader.##op##_Medium(); \
            break; \
        case LargeLayout: \
            reader.##op##_Large(); \
            break; \
        default: \
            Assert(false); \
            __assume(false); \
        } \
       saveBlock(); \
       break;     }
#define DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(op) \
        DEFAULT_LAYOUT_WITH_ONEBYTE(op); \
        DEFAULT_LAYOUT_WITH_ONEBYTE(Profiled##op)

                DEFAULT_LAYOUT(Empty);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg1);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Reg2);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Reg2WithICIndex);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Reg3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg5);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg3C);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Arg);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ArgNoSrc);
                DEFAULT_LAYOUT(Br);
#ifdef BYTECODE_BRANCH_ISLAND
                DEFAULT_LAYOUT(BrLong);
#endif
                DEFAULT_LAYOUT(BrS);
                DEFAULT_LAYOUT_WITH_ONEBYTE(BrReg1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(BrReg2);
                DEFAULT_LAYOUT(StartCall);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Profiled2CallI);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(CallI);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(CallIFlags);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(CallIWithICIndex);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(CallIFlagsWithICIndex);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementI);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementUnsigned1);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementSlot);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementSlotI1);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementSlotI2);
                DEFAULT_LAYOUT(W1);
                DEFAULT_LAYOUT(Reg1Int2);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Reg1Unsigned1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg2Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Unsigned1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementCP);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementRootCP);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementP);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementPIndexed);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg2B1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg3B1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Class);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementU);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementRootU);
                DEFAULT_LAYOUT(BrProperty);
                DEFAULT_LAYOUT(BrEnvProperty);
                DEFAULT_LAYOUT(BrLocalProperty);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementC2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementC);

#undef DEFAULT_LAYOUT
#undef DEFAULT_LAYOUT_WITH_ONEBYTE
                case OpLayoutType::AuxNoReg:
                    switch (op)
                    {
                        case OpCode::InitCachedFuncs:
                        {
                            auto layout = reader.AuxNoReg();
                            AuxRecord record = { sakFuncInfoArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::CommitScope:
                        {
                            // The propertyId array should be saved by the InitCacheScope
                            auto layout = reader.AuxNoReg();
                            Assert(layout);
                            saveBlock();
                            break;
                        }
                        default:
                            AssertMsg(false, "Unknown OpCode for OpLayoutType::AuxNoReg");
                            cantGenerate = true;
                            break;
                    }
                    break;
                case OpLayoutType::Auxiliary:
                    switch (op)
                    {
                        case OpCode::NewScObjectLiteral:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakPropertyIdArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::LdPropIds:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakPropertyIdArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::StArrSegItem_A:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakVarArrayIntCount, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::NewScObject_A:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakVarArrayVarCount, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::NewScIntArray:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakIntArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::NewScFltArray:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakFloatArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        default:
                            AssertMsg(false, "Unknown OpCode for OpLayoutType::Auxiliary");
                            cantGenerate = true;
                            break;
                    }
                    break;
                case OpLayoutType::ProfiledAuxiliary:
                    switch (op)
                    {
                        case OpCode::ProfiledNewScIntArray:
                        {
                            auto layout = reader.ProfiledAuxiliary();
                            AuxRecord record = { sakIntArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::ProfiledNewScFltArray:
                        {
                            auto layout = reader.ProfiledAuxiliary();
                            AuxRecord record = { sakFloatArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        default:
                            AssertMsg(false, "Unknown OpCode for OpLayoutType::ProfiledAuxiliary");
                            cantGenerate = true;
                            break;
                    }
                    break;
                case OpLayoutType::Reg2Aux:
                    switch (op)
                    {
                        case OpCode::SpreadArrayLiteral:
                        {
                            auto layout = reader.Reg2Aux();
                            AuxRecord record = { sakIntArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        default:
                            AssertMsg(false, "Unknown OpCode for OpLayoutType::Reg2Aux");
                            cantGenerate = true;
                            break;
                    }
                    break;

#define STORE_SPREAD_AUX_ARGS \
                    if (!(layout->Options & CallIExtended_SpreadArgs)) \
                    { \
                        break; \
                    } \
                    AuxRecord record = { sakIntArray, layout->SpreadAuxOffset }; \
                    auxRecords.Prepend(record)

#define CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(op) \
                    case OpLayoutType::##op: \
                    { \
                        switch (layoutSize) \
                        { \
                        case SmallLayout: \
                        { \
                            auto layout = reader.##op##_Small(); \
                            STORE_SPREAD_AUX_ARGS; \
                            break; \
                        } \
                        case MediumLayout: \
                        { \
                            auto layout = reader.##op##_Medium(); \
                            STORE_SPREAD_AUX_ARGS; \
                            break; \
                        } \
                        case LargeLayout: \
                        { \
                            auto layout = reader.##op##_Large(); \
                            STORE_SPREAD_AUX_ARGS; \
                            break; \
                        } \
                        default: \
                            Assert(false); \
                            __assume(false); \
                        } \
                       saveBlock(); \
                       break; \
                    }

                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(CallIExtended)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(CallIExtendedFlags)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(ProfiledCallIExtended)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(ProfiledCallIExtendedFlags)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(Profiled2CallIExtended)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(ProfiledCallIExtendedWithICIndex)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(ProfiledCallIExtendedFlagsWithICIndex)

                default:
                    AssertMsg(false, "Unknown OpLayout");
                    cantGenerate = true;
                    break;
            }
        }

        if (cantGenerate)
        {
            return ByteCodeSerializer::CantGenerate;
        }

        if (size != byteBlock->GetLength() && !GenerateByteCodeForNative(function->GetSerializationIndex()))
        {
            Assert(size == byteBlock->GetLength());
            return ByteCodeSerializer::CantGenerate;
        }
        finalSize->value = size;

        RewriteAuxiliaryInto(builder, auxRecords, reader, function);
        return S_OK;
    }


    void RewriteAuxiliaryInto(BufferBuilderList& builder, SListCounted<AuxRecord> const& auxRecordList,
        ByteCodeReader& reader, FunctionBody * functionBody)
    {
        uint count = auxRecordList.Count();
        PrependInt32(builder, L"Auxiliary Structure Count", count);
        if (count == 0)
        {
            return;
        }

        auto writeAuxVarArray = [&](uint offset, bool isVarCount, int count, const Var * elements)  {
            typedef serialization_alignment SerializedVarArray T;
            T header(offset, isVarCount, count);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, L"Var Array", header);
            builder.list = builder.list->Prepend(block, alloc);

            for (int i=0;i<count; i++)
            {
                auto var = elements[i];
                PrependVarConstant(builder, var);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, L"Magic end of aux var array", magicEndOfAuxVarArray);
            PrependInt32(builder, L"Magic end of aux", magicEndOfAux);
#endif
        };
        auto writeAuxVarArrayIntCount = [&](uint offset) {
            const AuxArray<Var> * varArray = reader.ReadAuxArray<Var>(offset, functionBody);
            int count = varArray->count;
            const Var * elements = varArray->elements;
            writeAuxVarArray(offset, false, count, elements);
        };
        auto writeAuxVarArrayVarCount = [&](uint offset) {
            const VarArrayVarCount * varArray = reader.ReadVarArrayVarCount(offset, functionBody);
            int count = Js::TaggedInt::ToInt32(varArray->count);
            const Var * elements = varArray->elements;
            writeAuxVarArray(offset, true, count, elements);
        };
        auto writeAuxIntArray = [&](uint offset) -> BufferBuilder* {
            const AuxArray<int32> *ints = reader.ReadAuxArray<int32>(offset, functionBody);
            int count = ints->count;
            const int32 * elements = ints->elements;

            typedef serialization_alignment SerializedIntArray T;
            T header(offset, count);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, L"Int Array", header);
            builder.list = builder.list->Prepend(block, alloc);

            for (int i=0;i<count; i++)
            {
                auto value = elements[i];
                PrependConstantInt32(builder, L"Integer Constant Value", value);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, L"Magic end of aux int array", magicEndOfAuxIntArray);
            PrependInt32(builder, L"Magic end of aux", magicEndOfAux);
#endif
            return block;
        };

        auto writeAuxFloatArray = [&](uint offset) -> BufferBuilder* {
            const AuxArray<double> *doubles = reader.ReadAuxArray<double>(offset, functionBody);
            int count = doubles->count;
            const double * elements = doubles->elements;

            typedef serialization_alignment SerializedFloatArray T;
            T header(offset, count);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, L"Float Array", header);
            builder.list = builder.list->Prepend(block, alloc);

            for (int i=0;i<count; i++)
            {
                auto value = elements[i];
                PrependDouble(builder, L"Number Constant Value", value);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, L"Magic end of aux float array", magicEndOfAuxFltArray);
            PrependInt32(builder, L"Magic end of aux", magicEndOfAux);
#endif
            return block;
        };

        auto writeAuxPropertyIdArray = [&](uint offset, uint32 extraSlots) -> BufferBuilder* {
            const PropertyIdArray * propIds = reader.ReadPropertyIdArray(offset, functionBody, extraSlots);

            typedef serialization_alignment SerializedPropertyIdArray T;
            T header(offset, propIds->count, extraSlots, propIds->hadDuplicates, propIds->has__proto__);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, L"Property Id Array", header);
            builder.list = builder.list->Prepend(block, alloc);

            for (uint32 i=0; i<propIds->count; i++)
            {
                auto original = propIds->elements[i];
                auto encoded = encodePossiblyBuiltInPropertyId(original);
                PrependConstantInt32(builder, L"Encoded Property Id", encoded);
            }
            auto slots = propIds->elements + propIds->count;
            for(uint32 i=0; i<extraSlots; i++)
            {
                PrependConstantInt32(builder, L"Extra Slot", slots[i]);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, L"Magic end of aux section", magicEndOfAuxPropIdArray);
            PrependInt32(builder, L"Magic end of aux", magicEndOfAux);
#endif
            return block;
        };

        auto writeAuxFuncInfoArray = [&](uint offset) -> BufferBuilder* {
            const FuncInfoArray * funcInfos = reader.ReadAuxArray<FuncInfoEntry>(offset, functionBody);

            typedef serialization_alignment SerializedFuncInfoArray T;
            T header(offset, funcInfos->count);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, L"Funcinfo Array", header);
            builder.list = builder.list->Prepend(block, alloc);

            for (uint32 i=0; i<funcInfos->count; i++)
            {
                auto funcInfo = funcInfos->elements[i];
                PrependConstantInt32(builder, L"FuncInfo nestedIndex", funcInfo.nestedIndex);
                PrependConstantInt32(builder, L"FuncInfo scopeSlot", funcInfo.scopeSlot);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, L"Magic end of aux section", magicEndOfAuxFuncInfoArray);
            PrependInt32(builder, L"Magic end of aux", magicEndOfAux);
#endif
            return block;
        };

        PrependInt32(builder, L"Auxiliary Size",
            functionBody->GetAuxiliaryData()? functionBody->GetAuxiliaryData()->GetLength() : 0);
        PrependInt32(builder, L"Auxiliary Context Size",
            functionBody->GetAuxiliaryContextData()? functionBody->GetAuxiliaryContextData()->GetLength() : 0);
        auxRecordList.Map([&](AuxRecord const& auxRecord)
        {
            switch (auxRecord.kind)
            {
            default:
                AssertMsg(false, "Unexpected auxiliary kind");
                Throw::FatalInternalError();
                break;

            case sakVarArrayIntCount:
                writeAuxVarArrayIntCount(auxRecord.offset);
                break;

            case sakVarArrayVarCount:
                writeAuxVarArrayVarCount(auxRecord.offset);
                break;

            case sakIntArray:
                writeAuxIntArray(auxRecord.offset);
                break;

            case sakFloatArray:
                writeAuxFloatArray(auxRecord.offset);
                break;

            case sakPropertyIdArray:
                writeAuxPropertyIdArray(auxRecord.offset, 0);
                break;

            case sakPropertyIdArrayForCachedScope:
                writeAuxPropertyIdArray(auxRecord.offset, ActivationObjectEx::ExtraSlotCount());
                break;

            case sakFuncInfoArray:
                writeAuxFuncInfoArray(auxRecord.offset);
                break;
            };
        });
    }

    uint32 PrependStringConstant(BufferBuilderList & builder, Var var)
    {
        auto str = JavascriptString::FromVar(var);
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"Start String Constant", magicStartStringConstant);
#endif

        auto bb = Anew(alloc, ByteBuffer, (str->GetLength() + 1) * sizeof(wchar_t), (void*)str->GetSz());
        size += PrependByteBuffer(builder, L"String Constant 16 Value", bb);

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End String Constant", magicEndStringConstant);
#endif

        return size;
    }

    uint32 PrependStringTemplateCallsiteConstant(BufferBuilderList & builder, Var var)
    {
        ES5Array* callsite = ES5Array::FromVar(var);
        Var element = nullptr;
        auto size = PrependInt32(builder, L"String Template Callsite Constant String Count", (int)callsite->GetLength());

        for (uint32 i = 0; i < callsite->GetLength(); i++)
        {
            callsite->DirectGetItemAt(i, &element);
            size += PrependStringConstant(builder, element);
        }

        Var rawVar = JavascriptOperators::OP_GetProperty(callsite, Js::PropertyIds::raw, callsite->GetScriptContext());
        ES5Array* rawArray = ES5Array::FromVar(rawVar);

        for (uint32 i = 0; i < rawArray->GetLength(); i++)
        {
            rawArray->DirectGetItemAt(i, &element);
            size += PrependStringConstant(builder, element);
        }

        return size;
    }

    uint32 PrependVarConstant(BufferBuilderList & builder, Var var)
    {
        if (var == (Js::Var)&Js::NullFrameDisplay)
        {
            return PrependByte(builder, L"Null Frame Display", ctNullDisplay);
        }
        else if (var == (Js::Var)&Js::StrictNullFrameDisplay)
        {
            return PrependByte(builder, L"Strict Null Frame Display", ctStrictNullDisplay);
        }

        auto typeId = JavascriptOperators::GetTypeId(var);
        switch (typeId)
        {
        case TypeIds_Undefined:
            return PrependByte(builder, L"Undefined Constant", ctUndefined);

        case TypeIds_Null:
            return PrependByte(builder, L"Null Constant", ctNull);

        case TypeIds_Boolean:
            return PrependByte(builder, L"Boolean Constant", JavascriptBoolean::FromVar(var)->GetValue()? ctTrue : ctFalse);

        case TypeIds_Number:
        {
            auto size = PrependByte(builder, L"Number Constant", ctNumber);
            return size + PrependDouble(builder,  L"Number Constant Value", JavascriptNumber::GetValue(var));
        }

        case TypeIds_Integer:
        {
            auto size = PrependByte(builder, L"Integer Constant", ctInt);
            return size + PrependConstantInt32(builder,  L"Integer Constant Value", TaggedInt::ToInt32(var));
        }

        case TypeIds_String:
        {
            auto size = PrependByte(builder, L"String Constant 16", ctString16);
            return size + PrependStringConstant(builder, var);
        }

        case TypeIds_ES5Array:
        {
            // ES5Array objects in the constant table are always string template callsite objects.
            // If we later put other ES5Array objects in the constant table, we'll need another way
            // to decide the constant type.
            auto size = PrependByte(builder, L"String Template Callsite Constant", ctStringTemplateCallsite);
            return size + PrependStringTemplateCallsiteConstant(builder, var);
        }

        default:
            AssertMsg(UNREACHED, "Unexpected object type in AddConstantTable");
            Throw::FatalInternalError();
        }
    }

#ifndef TEMP_DISABLE_ASMJS
    uint32 AddAsmJsConstantTable(BufferBuilderList & builder, FunctionBody * function)
    {
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"Start Constant Table", magicStartOfConstantTable);
#endif

        uint32 intConstCount = function->GetAsmJsFunctionInfo()->GetIntConstCount();
        uint32 floatConstCount = function->GetAsmJsFunctionInfo()->GetFloatConstCount();
        uint32 doubleConstCount = function->GetAsmJsFunctionInfo()->GetDoubleConstCount();
        Js::Var * constTable = static_cast<Js::Var *>(function->GetConstTable());

        int * intConstTable = reinterpret_cast<int *>(constTable + Js::AsmJsFunctionMemory::RequiredVarConstants - 1);
        for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < intConstCount; ++reg)
        {
            PrependConstantInt32(builder, L"Integer Constant Value", intConstTable[reg]);
        }

        float * floatConstTable = reinterpret_cast<float *>(intConstTable + intConstCount);

        for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < floatConstCount; ++reg)
        {
            PrependFloat(builder, L"Float Constant Value", floatConstTable[reg]);
        }

        double * doubleConstTable = reinterpret_cast<double *>(floatConstTable + floatConstCount);

        for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < doubleConstCount; ++reg)
        {
            PrependDouble(builder, L"Double Constant Value", doubleConstTable[reg]);
        }

        uint32 simdConstCount = function->GetAsmJsFunctionInfo()->GetSimdConstCount();
        AsmJsSIMDValue *simdConstTable = reinterpret_cast<AsmJsSIMDValue *>(doubleConstTable + doubleConstCount);

        for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < simdConstCount; ++reg)
        {
            PrependSIMDValue(builder, L"SIMD Constant Value", simdConstTable[reg]);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End Constant Table", magicEndOfConstantTable);
#endif

        return size;
    }
#endif

    uint32 AddConstantTable(BufferBuilderList & builder, FunctionBody * function)
    {
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"Start Constant Table", magicStartOfConstantTable);
#endif

        for (auto reg = FunctionBody::FirstRegSlot + 1; reg < function->m_constCount; reg++) // Ignore first slot, it is always global object or module root object
        {
            auto var = function->GetConstantVar(reg);
            Assert(var != nullptr);
            size += PrependVarConstant(builder, var);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End Constant Table", magicEndOfConstantTable);
#endif

        return size;
    }

    uint32 AddPropertyIdsForScopeSlotArray(BufferBuilderList & builder, FunctionBody * function)
    {
        if (function->scopeSlotArraySize == 0)
        {
            return 0;
        }
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"Start PropertyIdsForScopeSlotsArray", magicStartOfPropertyIdsForScopeSlotArray);
#endif

        for (uint i = 0; i < function->scopeSlotArraySize; i++)
        {
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(function->GetPropertyIdsForScopeSlotArray()[i]);
            size += PrependInt32(builder, L"PropertyIdsForScopeSlots", propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End PropertyIdsForScopeSlotsArray", magicEndOfPropertyIdsForScopeSlotArray);
#endif

        return size;
    }

    // Gets the number of debugger slot array scopes there are in the function body's scope chain list.
    uint32 GetDebuggerScopeSlotArrayCount(FunctionBody * function)
    {
        Assert(function);
        uint debuggerScopeSlotArraySize = 0;
        if (function->GetScopeObjectChain())
        {
            debuggerScopeSlotArraySize = function->GetScopeObjectChain()->pScopeChain->CountWhere([&](DebuggerScope* scope)
            {
                return scope->IsSlotScope();
            });
        }

        return debuggerScopeSlotArraySize;
    }

    // Adds the debugger scopes that are slot array type to the serialized bytecode.
    // This is to ensure that block scope slot array properties are captured along with
    // function level slot array properties.
    uint32 AddSlotArrayDebuggerScopeProperties(BufferBuilderList & builder, DebuggerScope* debuggerScope, uint propertiesCount)
    {
        Assert(debuggerScope);
        if (propertiesCount == 0)
        {
            return 0u;
        }

        uint32 size = 0u;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"Start SlotArrayDebuggerScopeProperties", magicStartOfDebuggerScopeProperties);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        AssertMsg(debuggerScope->HasProperties(), "Properties should exist.");
        Assert(debuggerScope->scopeProperties->Count() >= 0);
        AssertMsg((uint)debuggerScope->scopeProperties->Count() == propertiesCount, "Property counts should match.");
        for (uint i = 0u; i < propertiesCount; ++i)
        {
            DebuggerScopeProperty scopeProperty = debuggerScope->scopeProperties->Item(i);
            size += PrependInt32(builder, L"SlotIndexesForDebuggerScopeSlots", scopeProperty.location);

            PropertyId propertyId = encodePossiblyBuiltInPropertyId(scopeProperty.propId);
            size += PrependInt32(builder, L"PropertyIdsForDebuggerScopeSlots", propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End SlotArrayDebuggerScopeProperties", magicEndOfDebuggerScopeProperties);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        return size;
    }

    // Adds the debugger scopes that are slot array type to the serialized bytecode.
    // This is to ensure that block scope slot array properties are captured along with
    // function level slot array properties.
    uint32 AddSlotArrayDebuggerScopes(BufferBuilderList & builder, FunctionBody* function, uint debuggerScopeSlotArraySize)
    {
        Assert(function);
        if (function->GetScopeObjectChain() == nullptr || debuggerScopeSlotArraySize == 0)
        {
            return 0u;
        }

        uint32 size = 0u;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"Start SlotArrayDebuggerScopes", magicStartOfDebuggerScopes);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        uint slotArrayCount = 0;
        for (uint i = 0u; i < static_cast<uint>(function->GetScopeObjectChain()->pScopeChain->Count()); ++i)
        {
            DebuggerScope* debuggerScope = function->GetScopeObjectChain()->pScopeChain->Item(i);
            if (debuggerScope->IsSlotScope())
            {
                // Only add slot scope debugger scopes (store the index of the scope).
                size += PrependInt32(builder, L"SlotArrayDebuggerScope", i);

                // Store the count of properties for the scope.
                int propertiesCount = debuggerScope->HasProperties() ? debuggerScope->scopeProperties->Count() : 0u;
                size += PrependInt32(builder, L"Debugger Scope Slot Array Property Count", propertiesCount);
                size += AddSlotArrayDebuggerScopeProperties(builder, debuggerScope, propertiesCount);
                slotArrayCount++;
            }
        }
        Assert(debuggerScopeSlotArraySize == slotArrayCount);

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End SlotArrayDebuggerScopes", magicEndOfDebuggerScopes);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        return size;
    }

    uint32 AddCacheIdToPropertyIdMap(BufferBuilderList & builder, FunctionBody * function)
    {
        uint count = function->GetInlineCacheCount();
        if (count == 0)
        {
            return 0;
        }
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, L"Start CacheId-to-PropertyId map", magicStartOfCacheIdToPropIdMap);
#endif

        for (uint i = 0; i < count; i++)
        {
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(function->GetPropertyIdFromCacheId(i));
            size += PrependInt32(builder, L"CacheIdToPropertyId", propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End CacheId-to-PropertyId map", magicEndOfCacheIdToPropIdMap);
#endif

        return size;
    }

    uint32 AddReferencedPropertyIdMap(BufferBuilderList & builder, FunctionBody * function)
    {
        uint count = function->GetReferencedPropertyIdCount();
        if (count == 0)
        {
            return 0;
        }
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, L"Start Referenced-PropertyId map", magicStartOfReferencedPropIdMap);
#endif

        for (uint i = 0; i < count; i++)
        {
            PropertyId propertyId = encodeNonBuiltinPropertyId(function->GetReferencedPropertyIdWithMapIndex(i));
            size += PrependInt32(builder, L"ReferencedPropertyId", propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End Referenced-PropertyId map", magicEndOfReferencedPropIdMap);
#endif

        return size;
    }

    uint32 PrependByteArray(BufferBuilderList & builder, int length, byte * buffer)
    {
        int size = 0;
        for (int i = 0; i<length; ++i)
        {
            size += PrependByte(builder, L"Byte Array Element", buffer[i]);
        }
        return size;
    }

    uint32 PrependUInt32Array(BufferBuilderList & builder, int length, uint32 * buffer)
    {
        int size = 0;
        for(int i=0;i<length;++i)
        {
            size += PrependConstantInt32(builder, L"UInt32 Array Element", buffer[i]);
        }
        return size;
    }

    uint32 PrependGrowingUint32Array(BufferBuilderList & builder, LPCWSTR clue, JsUtil::GrowingUint32HeapArray * arr)
    {
        if (arr == nullptr || arr->Count() == 0 || arr->GetLength() == 0 || arr->GetBuffer() == nullptr)
        {
            return PrependInt32(builder, clue, 0);
        }
        auto size = PrependInt32(builder, clue, arr->Count());
        size += PrependUInt32Array(builder, arr->Count(), arr->GetBuffer());
        return size;
    }

    uint32 PrependSmallSpanSequence(BufferBuilderList & builder, LPCWSTR clue, SmallSpanSequence * spanSequence)
    {
        auto size = PrependInt32(builder, clue, spanSequence->baseValue);
        size += PrependGrowingUint32Array(builder, L"Statement Buffer", spanSequence->pStatementBuffer);
        size += PrependGrowingUint32Array(builder, L"Actual Offset List", spanSequence->pActualOffsetList);
        return size;
    }

    template <typename TStructType>
    uint32 PrependStruct(BufferBuilderList & builder, LPWSTR clue, TStructType * value)
    {
        auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<TStructType>, clue, *value);
        builder.list = builder.list->Prepend(entry, alloc);

        return sizeof(serialization_alignment TStructType);
    }

#ifndef TEMP_DISABLE_ASMJS
    uint32 AddAsmJsFunctionInfo(BufferBuilderList & builder, FunctionBody * function)
    {
        uint32 size = 0;
        AsmJsFunctionInfo* funcInfo = function->GetAsmJsFunctionInfo();

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, L"Start Asm.js Function Info", magicStartOfAsmJsFuncInfo);
#endif
        size += PrependInt32(builder, L"ReturnType", funcInfo->GetReturnType().which());
        size += PrependInt32(builder, L"IntConstCount", funcInfo->GetIntConstCount());
        size += PrependInt32(builder, L"DoubleConstCount", funcInfo->GetDoubleConstCount());
        size += PrependInt32(builder, L"FloatConstCount", funcInfo->GetFloatConstCount());
        size += PrependInt16(builder, L"ArgCount", funcInfo->GetArgCount());
        size += PrependInt16(builder, L"ArgSize", funcInfo->GetArgByteSize());
        size += PrependInt32(builder, L"IntVarCount", funcInfo->GetIntVarCount());
        size += PrependInt32(builder, L"DoubleVarCount", funcInfo->GetDoubleVarCount());
        size += PrependInt32(builder, L"FloatVarCount", funcInfo->GetFloatVarCount());
        size += PrependInt32(builder, L"IntTmpCount", funcInfo->GetIntTmpCount());
        size += PrependInt32(builder, L"DoubleTmpCount", funcInfo->GetDoubleTmpCount());
        size += PrependInt32(builder, L"FloatTmpCount", funcInfo->GetFloatTmpCount());
        size += PrependInt16(builder, L"ArgSizeArrayLength", funcInfo->GetArgSizeArrayLength());
        size += PrependUInt32Array(builder, funcInfo->GetArgSizeArrayLength(), funcInfo->GetArgsSizesArray());
        size += PrependByteArray(builder, funcInfo->GetArgCount(), (byte*)funcInfo->GetArgTypeArray());
        size += PrependInt32(builder, L"IntByteOffset", funcInfo->GetIntByteOffset());
        size += PrependInt32(builder, L"DoubleByteOffset", funcInfo->GetDoubleByteOffset());
        size += PrependInt32(builder, L"FloatByteOffset", funcInfo->GetFloatByteOffset());
        size += PrependByte(builder, L"IsHeapBufferConst", funcInfo->IsHeapBufferConst());
        size += PrependByte(builder, L"UsesHeapBuffer", funcInfo->UsesHeapBuffer());
        size += PrependInt32(builder, L"SIMDConstCount", funcInfo->GetSimdConstCount());
        size += PrependInt32(builder, L"SIMDVarCount", funcInfo->GetSimdVarCount());
        size += PrependInt32(builder, L"SIMDTmpCount", funcInfo->GetSimdTmpCount());
        size += PrependInt32(builder, L"SIMDByteOffset", funcInfo->GetSimdByteOffset());

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End Asm.js Function Info", magicEndOfAsmJsFuncInfo);
#endif

        return size;
    }

    uint32 AddAsmJsModuleInfo(BufferBuilderList & builder, FunctionBody * function)
    {
        uint32 size = 0;
        AsmJsModuleInfo * moduleInfo = function->GetAsmJsModuleInfo();

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, L"Start Asm.js Module Info", magicStartOfAsmJsModuleInfo);
#endif

        size += PrependInt32(builder, L"ArgInCount", moduleInfo->GetArgInCount());
        size += PrependInt32(builder, L"ExportsCount", moduleInfo->GetExportsCount());
        size += PrependInt32(builder, L"SlotsCount", moduleInfo->GetSlotsCount());
        size += PrependInt32(builder, L"SIMDRegCount", moduleInfo->GetSimdRegCount());

        if (moduleInfo->GetExportsCount() > 0)
        {
            PropertyIdArray * propArray = moduleInfo->GetExportsIdArray();
            size += PrependByte(builder, L"ExportsIdArrayDups", propArray->hadDuplicates);
            size += PrependByte(builder, L"ExportsIdArray__proto__", propArray->has__proto__);
            size += PrependInt32(builder, L"ExportsIdArrayLength", propArray->count);
            for (uint i = 0; i < propArray->count; i++)
            {
                PropertyId propertyId = encodePossiblyBuiltInPropertyId(propArray->elements[i]);
                size += PrependInt32(builder, L"ExportsIdArrayElem", propertyId);
            }
            size += PrependUInt32Array(builder, moduleInfo->GetExportsCount(), moduleInfo->GetExportsFunctionLocation());
        }

        size += PrependInt32(builder, L"ExportFunctionIndex", moduleInfo->GetExportFunctionIndex());

        size += PrependInt32(builder, L"VarCount", moduleInfo->GetVarCount());
        for (int i = 0; i < moduleInfo->GetVarCount(); i++)
        {
            size += PrependStruct(builder, L"ModuleVar", &moduleInfo->GetVar(i));
        }

        size += PrependInt32(builder, L"VarImportCount", moduleInfo->GetVarImportCount());
        for (int i = 0; i < moduleInfo->GetVarImportCount(); i++)
        {
            auto import = moduleInfo->GetVarImport(i);
            size += PrependInt32(builder, L"ImportLocation", import.location);
            size += PrependByte(builder, L"ImportType", import.type);
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(import.field);
            size += PrependInt32(builder, L"ImportId", propertyId);
        }

        size += PrependInt32(builder, L"FunctionImportCount", moduleInfo->GetFunctionImportCount());
        for (int i = 0; i < moduleInfo->GetFunctionImportCount(); i++)
        {
            auto import = moduleInfo->GetFunctionImport(i);
            size += PrependInt32(builder, L"ImportLocation", import.location);
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(import.field);
            size += PrependInt32(builder, L"ImportId", propertyId);
        }

        size += PrependInt32(builder, L"FunctionCount", moduleInfo->GetFunctionCount());
        for (int i = 0; i < moduleInfo->GetFunctionCount(); i++)
        {
            auto func = moduleInfo->GetFunction(i);
            size += PrependInt32(builder, L"FuncLocation", func.location);
        }

        size += PrependInt32(builder, L"FunctionTableCount", moduleInfo->GetFunctionTableCount());
        for (int i = 0; i < moduleInfo->GetFunctionTableCount(); i++)
        {
            auto table = moduleInfo->GetFunctionTable(i);
            size += PrependInt32(builder, L"FuncTableSize", table.size);
            PrependUInt32Array(builder, table.size, table.moduleFunctionIndex);
        }

        size += PrependStruct<AsmJsModuleMemory>(builder, L"ModuleMemory", &moduleInfo->GetModuleMemory());

        size += PrependInt32(builder, L"AsmJsSlotMapCount", moduleInfo->GetAsmJsSlotMap()->Count());
        auto slotIter = moduleInfo->GetAsmJsSlotMap()->GetIterator();
        while (slotIter.IsValid())
        {
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(slotIter.CurrentKey());
            size += PrependInt32(builder, L"AsmJsSlotPropId", propertyId);
            size += PrependStruct(builder, L"AsmJsSlotValue", slotIter.CurrentValue());
            slotIter.MoveNext();
        }
        size += PrependStruct(builder, L"MathBuiltinBV", &moduleInfo->GetAsmMathBuiltinUsed());
        size += PrependStruct(builder, L"ArrayBuiltinBV", &moduleInfo->GetAsmArrayBuiltinUsed());
        size += PrependStruct(builder, L"SIMDBuiltinBV", &moduleInfo->GetAsmSimdBuiltinUsed());

        size += PrependInt32(builder, L"MaxHeapAccess", moduleInfo->GetMaxHeapAccess());
        size += PrependByte(builder, L"UsesChangeHeap", moduleInfo->GetUsesChangeHeap());


#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, L"End Asm.js Module Info", magicEndOfAsmJsModuleInfo);
#endif
        return size;
    }
#endif

    HRESULT AddFunctionBody(BufferBuilderList & builder, FunctionBody * function, SRCINFO const * srcInfo, int *serializationIndex)
    {
        SerializedFieldList definedFields = { 0 };

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, L"Start Function Table", magicStartOfFunctionBody);
#endif

        function->SetSerializationIndex((*serializationIndex)++);

        uint32 sourceDiff = 0;

        if (!TryConvertToUInt32(function->StartOffset(), &sourceDiff))
        {
            Assert(0); // Likely a bug
            return ByteCodeSerializer::CantGenerate;
        }
        PrependString16(builder, L"Display Name", function->m_displayName, (function->m_displayNameLength +1)* sizeof(wchar_t));

        if (function->m_lineNumber != 0)
        {
            definedFields.has_m_lineNumber = true;
            PrependInt32(builder, L"Line Number", function->m_lineNumber);
        }

        if (function->m_columnNumber != 0)
        {
            definedFields.has_m_columnNumber = true;
            PrependInt32(builder, L"Column Number", function->m_columnNumber);
        }

        // FunctionBody Details
        DWORD bitFlags =
            (function->m_isDeclaration ? ffIsDeclaration : 0)
            | (function->m_hasImplicitArgIns ? ffHasImplicitArgsIn : 0)
            | (function->m_isAccessor ? ffIsAccessor : 0)
            | (function->m_isStaticNameFunction ? ffIsStaticNameFunction : 0)
            | (function->m_isNamedFunctionExpression ? ffIsNamedFunctionExpression : 0)
            | (function->m_isNameIdentifierRef ? ffIsNameIdentifierRef : 0)
            | (function->m_isGlobalFunc ? ffIsGlobalFunc : 0)
            | (function->m_dontInline ? ffDontInline : 0)
            | (function->m_isFuncRegistered ? ffIsFuncRegistered : 0)
            | (function->m_isStrictMode ? ffIsStrictMode : 0)
            | (function->m_doBackendArgumentsOptimization ? ffDoBackendArgumentsOptimization : 0)
            | (function->m_isEval ? ffIsEval : 0)
            | (function->m_isDynamicFunction ? ffIsDynamicFunction : 0)
            | (function->m_hasAllNonLocalReferenced ? ffhasAllNonLocalReferenced : 0)
            | (function->m_hasSetIsObject ? ffhasSetIsObject : 0)
            | (function->m_CallsEval ? ffhasSetCallsEval : 0)
            | (function->m_ChildCallsEval ? ffChildCallsEval : 0)
            | (function->m_hasReferenceableBuiltInArguments ? ffHasReferenceableBuiltInArguments : 0)
#ifndef TEMP_DISABLE_ASMJS
            | (function->m_isAsmjsMode ? ffIsAsmJsMode : 0)
            | (function->m_isAsmJsFunction ? ffIsAsmJsFunction : 0)
#endif
            ;

        PrependInt32(builder, L"BitFlags", bitFlags);
        PrependInt32(builder, L"Relative Function ID", function->functionId - topFunctionId); // Serialized function ids are relative to the top function ID
        PrependInt32(builder, L"Serialization ID", function->GetSerializationIndex());
        PrependInt32(builder, L"Attributes", function->GetAttributes());
        AssertMsg((function->GetAttributes() &
                ~(FunctionInfo::Attributes::ErrorOnNew
                  | FunctionInfo::Attributes::SuperReference
                  | FunctionInfo::Attributes::Lambda
                  | FunctionInfo::Attributes::Async
                  | FunctionInfo::Attributes::CapturesThis
                  | FunctionInfo::Attributes::Generator
                  | FunctionInfo::Attributes::ClassConstructor
                  | FunctionInfo::Attributes::ClassMethod)) == 0,
                "Only the ErrorOnNew|SuperReference|Lambda|CapturesThis|Generator|ClassConstructor|Async|ClassMember attributes should be set on a serialized function");

        PrependInt32(builder, L"Offset Into Source", sourceDiff);
        if (function->GetNestedCount() > 0)
        {
            definedFields.has_m_nestedCount = true;
            PrependInt32(builder, L"Nested count", function->GetNestedCount());
        }

        // This field should always be non-zero
        Assert(function->m_constCount != 0);

#define PrependArgSlot PrependInt16
#define PrependRegSlot PrependInt32
#define PrependCharCount PrependInt32
#define PrependULong PrependInt32
#define PrependUInt16 PrependInt16
#define PrependUInt32 PrependInt32

#define DEFINE_FUNCTION_PROXY_FIELDS 1
#define DEFINE_PARSEABLE_FUNCTION_INFO_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
        if (function->##name != 0) { \
            definedFields.has_##name = true; \
            Prepend##serializableType(builder, L#name, function->##name); \
        }
#define DECLARE_MANUAL_SERIALIZABLE_FIELD(type, name, serializableType, serializeHere) \
        if (function->##name != 0 && serializeHere) { \
            definedFields.has_##name = true; \
            Prepend##serializableType(builder, L#name, function->##name); \
        }

#include "SerializableFunctionFields.h"

        if (!OmitFunction(function->GetSerializationIndex()))
        {
#define DEFINE_FUNCTION_BODY_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
            if (function->##name != 0) { \
                definedFields.has_##name = true; \
                Prepend##serializableType(builder, L#name, function->##name); \
            }
#define DECLARE_MANUAL_SERIALIZABLE_FIELD(type, name, serializableType, serializeHere) \
            if (function->##name != 0 && serializeHere) { \
                definedFields.has_##name = true; \
                Prepend##serializableType(builder, L#name, function->##name); \
            }

#include "SerializableFunctionFields.h"
        }

        if (!OmitFunction(function->GetSerializationIndex()))
        {
            auto loopHeaderArray = function->GetLoopHeaderArray();
            if (loopHeaderArray)
            {
                PrependByte(builder, L"Loop Header Array Exists", 1);
                for (uint i = 0; i < function->loopCount; ++i)
                {
                    PrependInt32(builder, L"Loop Header Start", loopHeaderArray[i].startOffset);
                    PrependInt32(builder, L"Loop Header End", loopHeaderArray[i].endOffset);
                }
            }
            else
            {
                PrependByte(builder, L"Loop Header Array Exists", 0);
            }

#ifndef TEMP_DISABLE_ASMJS
            if (function->GetAsmJsFunctionInfo())
            {
                PrependByte(builder, L"Asm.js Info Exists", 1);
                AddAsmJsFunctionInfo(builder, function);
            }
            else if (function->GetIsAsmjsMode())
            {
                PrependByte(builder, L"Asm.js Info Exists", 2);
                AddAsmJsModuleInfo(builder, function);
            }
            else
#endif
            {
                PrependByte(builder, L"Asm.js Info Exists", 0);
            }

#ifndef TEMP_DISABLE_ASMJS
            if (function->GetIsAsmJsFunction())
            {
                AddAsmJsConstantTable(builder, function);
                auto hr = RewriteAsmJsByteCodesInto(builder, L"Rewritten Byte Code", function, function->byteCodeBlock);
                if (FAILED(hr))
                {
                    return hr;
                }
            }
            else
#endif
            {
                AddConstantTable(builder, function);
                auto hr = RewriteByteCodesInto(builder, L"Rewritten Byte Code", function, function->byteCodeBlock);
                if (FAILED(hr))
                {
                    return hr;
                }
            }


            AddCacheIdToPropertyIdMap(builder, function);
            AddReferencedPropertyIdMap(builder, function);

            AddPropertyIdsForScopeSlotArray(builder, function);

            uint debuggerScopeSlotArraySize = GetDebuggerScopeSlotArrayCount(function);
            PrependInt32(builder, L"Debugger Scope Slot Array Size", debuggerScopeSlotArraySize);
            AddSlotArrayDebuggerScopes(builder, function, debuggerScopeSlotArraySize);

            // Literal regexes
            for (uint i = 0; i < function->literalRegexCount; ++i)
            {
                const auto literalRegex = function->GetLiteralRegex(i);
                if (!literalRegex)
                {
                    PrependInt32(builder, L"Literal regex source length", -1);
                    continue;
                }

                const auto source = literalRegex->GetSource();
                PrependInt32(builder, L"Literal regex source length", source.GetLength());
                PrependString16(builder, L"Literal regex source", source.GetBuffer(), (source.GetLength() + 1)* sizeof(wchar_t));
                PrependByte(builder, L"Literal regex flags", literalRegex->GetFlags());
            }

            // Write the SourceInfo stuff
            PrependSmallSpanSequence(builder, L"Span Sequence", function->m_sourceInfo.pSpanSequence);
        }

        // Lastly, write each of the lexically enclosed functions
        if (function->GetNestedCount())
        {
            auto nestedBodyList = Anew(alloc, BufferBuilderList, L"Nest Function Bodies");

            for(uint32 i = 0; i<function->GetNestedCount(); ++i)
            {
                auto nestedFunctionBody = function->GetNestedFunc(i)->GetFunctionBody();
                if (nestedFunctionBody==nullptr)
                {
                    PrependInt32(builder, L"Empty Nested Function", 0);
                }
                else
                {
                    auto nestedFunctionBuilder = Anew(alloc, BufferBuilderList, L"Nested Function");
                    nestedBodyList->list = nestedBodyList->list->Prepend(nestedFunctionBuilder, alloc);
                    auto offsetToNested = Anew(alloc, BufferBuilderRelativeOffset, L"Offset To Nested Function", nestedFunctionBuilder);
                    builder.list = builder.list->Prepend(offsetToNested, alloc);
                    AddFunctionBody(*nestedFunctionBuilder, nestedFunctionBody, srcInfo, serializationIndex);
                }
            }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, L"End Function Body", magicEndOfFunctionBody);
#endif

            builder.list = builder.list->Prepend(nestedBodyList, alloc);
        }
        else
        {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, L"End Function Body", magicEndOfFunctionBody);
#endif
        }


        // Increment the function count
        ++functionCount.value;

        // Reverse to put prepended items in correct order
        builder.list = builder.list->ReverseCurrentList();
        PrependStruct<SerializedFieldList>(builder, L"Serialized Field List", &definedFields);

        return S_OK;
    }

    HRESULT AddTopFunctionBody(FunctionBody * function, SRCINFO const * srcInfo)
    {
        topFunctionId = function->functionId;
        int serializationIndex = 0;
        return AddFunctionBody(functionsTable, function, srcInfo, &serializationIndex);
    }

};

class ByteCodeBufferReader
{
public:
    ScriptContext * scriptContext;
    byte * raw;
    int magic;
    int totalSize;
    byte fileVersionScheme;
    int V1;
    int V2;
    int V3;
    int V4;
    byte architecture;
    int expectedFunctionBodySize;
    int expectedBuildInPropertyCount;
    int expectedOpCodeCount;
    int firstFunctionId;
    int functionCount;
    const byte * string16s;
    int string16Count;
    const unaligned StringIndexRecord * string16IndexTable;
    const byte * string16Table;
    const byte * sourceSpans;
    int lineInfoCacheCount;
    const byte * lineInfoCaches;
    const JsUtil::LineOffsetCache<Recycler>::LineOffsetCacheItem * lineInfoCache;
    const byte * functions;
    int sourceSize;
    int sourceCharLength;
    Utf8SourceInfo *utf8SourceInfo;
    uint sourceIndex;
    bool const isLibraryCode;
public:
    ByteCodeBufferReader(ScriptContext * scriptContext, byte * raw, bool isLibraryCode, int builtInPropertyCount)
        : scriptContext(scriptContext), raw(raw), utf8SourceInfo(nullptr), isLibraryCode(isLibraryCode),
        expectedFunctionBodySize(sizeof(unaligned FunctionBody)),
        expectedBuildInPropertyCount(builtInPropertyCount),
        expectedOpCodeCount((int)OpCode::Count)
    {
        if (isLibraryCode)
        {
            expectedFunctionBodySize = 0;
            expectedOpCodeCount = 0;
        }
    }

    static const byte* ReadFunctionBodyFlags(const byte * buffer, size_t remainingBytes, FunctionBody::FunctionBodyFlags * value)
    {
        Assert(remainingBytes >= sizeof(FunctionBody::FunctionBodyFlags));
        *value = *(FunctionBody::FunctionBodyFlags*) buffer;
        return buffer + sizeof(FunctionBody::FunctionBodyFlags);
    }

    const byte* ReadFunctionBodyFlags(const byte * buffer, FunctionBody::FunctionBodyFlags * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadFunctionBodyFlags(buffer, remainingBytes, value);
    }

    const byte* ReadBool(const byte * buffer, bool * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        Assert(remainingBytes >= sizeof(bool));
        *value = *buffer ? true : false;
        return buffer + sizeof(bool);
    }

    static const byte * ReadByte(const byte * buffer, size_t remainingBytes, byte * value)
    {
        Assert(remainingBytes>=sizeof(byte));
        *value = *(byte*)buffer;
        return buffer + sizeof(byte);
    }

    const byte * ReadByte(const byte * buffer, byte * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadByte(buffer, remainingBytes, value);
    }

    static const byte * ReadInt16(const byte * buffer, size_t remainingBytes, int16 * value)
    {
#if VARIABLE_INT_ENCODING
        return ReadVariableInt<int16>(buffer, remainingBytes, value);
#else
        Assert(remainingBytes>=sizeof(int16));
        *value = *(int16 *) buffer;
        return buffer + sizeof(int16);
#endif
    }

    const byte * ReadInt16(const byte * buffer, int16 * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt16(buffer, remainingBytes, value);
    }

    static const byte * ReadConstantSizedInt64(const byte * buffer, size_t remainingBytes, int64 * value)
    {
        Assert(remainingBytes >= sizeof(int64));
        *value = *(int64 *)buffer;
        return buffer + sizeof(int64);
    }

    const byte * ReadConstantSizedInt64(const byte * buffer, int64 * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadConstantSizedInt64(buffer, remainingBytes, value);
    }

    static const byte * ReadConstantSizedInt32(const byte * buffer, size_t remainingBytes, int * value)
    {
        Assert(remainingBytes >= sizeof(int));
        *value = *(int *) buffer;
        return buffer + sizeof(int);
    }

    const byte * ReadConstantSizedInt32(const byte * buffer, int * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadConstantSizedInt32(buffer, remainingBytes, value);
    }

    static const byte * ReadInt32(const byte * buffer, size_t remainingBytes, int * value)
    {
#if VARIABLE_INT_ENCODING
        return ReadVariableInt<int>(buffer, remainingBytes, value);
#else
        Assert(remainingBytes >= sizeof(int));
        return ReadConstantSizedInt32(buffer, remainingBytes, value);
#endif
    }

    const byte * ReadInt32(const byte * buffer, int * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, value);
    }

    const byte * ReadCharCount(const byte * buffer, size_t remainingBytes, charcount_t * value)
    {
        Assert(remainingBytes>=sizeof(charcount_t));
#if VARIABLE_INT_ENCODING
        return ReadVariableInt<charcount_t>(buffer, remainingBytes, value);
#else
        *value = *(charcount_t *) buffer;
        return buffer + sizeof(charcount_t);
#endif
    }

    const byte * ReadCharCount(const byte * buffer, charcount_t * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadCharCount(buffer, remainingBytes, value);
    }

    static const byte * ReadFloat(const byte * buffer, size_t remainingBytes, float * value)
    {
        Assert(remainingBytes >= sizeof(float));
        *value = *(float *)buffer;
        return buffer + sizeof(float);
    }

    const byte * ReadFloat(const byte * buffer, float * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadFloat(buffer, remainingBytes, value);
    }

    static const byte * ReadDouble(const byte * buffer, size_t remainingBytes, double * value)
    {
        Assert(remainingBytes>=sizeof(double));
        *value = *(double *)buffer;
        return buffer + sizeof(double);
    }

    const byte * ReadDouble(const byte * buffer, double * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadDouble(buffer, remainingBytes, value);
    }

    static const byte * ReadSIMDValue(const byte * buffer, size_t remainingBytes, SIMDValue * value)
    {
        Assert(remainingBytes >= sizeof(SIMDValue));
        *value = *(SIMDValue *)buffer;
        return buffer + sizeof(SIMDValue);
    }

    const byte * ReadSIMDValue(const byte * buffer, SIMDValue * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadSIMDValue(buffer, remainingBytes, value);
    }

    const byte * ReadUInt16(const byte * buffer, uint16 * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt16(buffer, remainingBytes, (int16*)value);
    }

    const byte * ReadUInt32(const byte * buffer, unsigned int * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, (int*)value);
    }

    const byte * ReadULong(const byte * buffer, ulong * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, (int*)value);
    }

    const byte * ReadRegSlot(const byte * buffer, RegSlot * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, (int*)value);
    }

    const byte * ReadArgSlot(const byte * buffer, ArgSlot * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, (int*)value);
    }

    const byte * ReadConstantSizedInt32NoSize(const byte * buffer, int * value)
    {
        *value = *(int *)buffer;
        return buffer + sizeof(int);
    }

    const byte * ReadOffsetAsPointer(const byte * buffer, byte const ** value)
    {
        int offset;
        auto next = ReadConstantSizedInt32(buffer, &offset);
        if (offset == 0)
        {
            *value = nullptr;
            return next;
        }
        *value = raw + offset;
        return next;
    }

    const byte * ReadByteBlock(const byte * buffer, WriteBarrierPtr<ByteBlock>* byteBlock)
    {
        int contentLength;
        buffer = ReadInt32(buffer, &contentLength);

        if (contentLength == 0)
        {
            *byteBlock = nullptr;
        }
        else
        {
            // TODO: Abstract this out to ByteBlock::New
            *byteBlock = RecyclerNewLeaf(scriptContext->GetRecycler(), ByteBlock, contentLength, (byte*)buffer);
        }
        return buffer + contentLength;
    }

    const byte * ReadAuxiliary(const byte * buffer, FunctionBody * functionBody)
    {
        const byte * current = buffer;
        uint32 countOfAuxiliaryStructure;
        current = ReadUInt32(current, &countOfAuxiliaryStructure);
        if (countOfAuxiliaryStructure == 0)
        {
            return current;
        }
        uint32 sizeOfAuxiliaryBlock;
        uint32 sizeOfAuxiliaryContextBlock;
        current = ReadUInt32(current, &sizeOfAuxiliaryBlock);
        current = ReadUInt32(current, &sizeOfAuxiliaryContextBlock);

        ByteBlock * auxBlock = sizeOfAuxiliaryBlock?
            ByteBlock::New(scriptContext->GetRecycler(), nullptr, sizeOfAuxiliaryBlock) : nullptr;
        ByteBlock * auxContextBlock = sizeOfAuxiliaryContextBlock?
            ByteBlock::New(scriptContext->GetRecycler(), nullptr, sizeOfAuxiliaryContextBlock) : nullptr;

        for (uint i = 0; i < countOfAuxiliaryStructure; i++)
        {
            typedef serialization_alignment const SerializedAuxiliary TBase;
            auto part = (serialization_alignment const SerializedAuxiliary * )current;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            Assert(part->auxMagic == magicStartOfAux);
#endif
            switch(part->kind)
            {
            default:
                AssertMsg(false, "Unexpected auxiliary kind");
                Throw::FatalInternalError();
                break;

            case sakVarArrayIntCount:
                current = DeserializeVarArray<VarArray>(scriptContext, current, auxBlock);
                break;

            case sakVarArrayVarCount:
                current = DeserializeVarArray<VarArrayVarCount>(scriptContext, current, auxContextBlock);
                break;

            case sakIntArray:
                current = DeserializeIntArray(scriptContext, current, auxBlock);
                break;

            case sakFloatArray:
                current = DeserializeFloatArray(scriptContext, current, auxBlock);
                break;

            case sakPropertyIdArray:
                current = DeserializePropertyIdArray(scriptContext, current, auxBlock, functionBody);
                break;

            case sakFuncInfoArray:
                current = DeserializeFuncInfoArray(scriptContext, current, auxBlock);
                break;
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            int magicEnd;
            current = ReadInt32(current, &magicEnd);
            Assert(magicEnd == magicEndOfAux);
#endif
        }
        functionBody->SetAuxiliaryData(auxBlock);
        functionBody->SetAuxiliaryContextData(auxContextBlock);

        return current;
    }

    LPCWSTR GetString16ById(int id, bool* isPropertyRecord = nullptr)
    {
        if (id == 0xffffffff)
        {
            return nullptr;
        }
        if(!(id >= this->expectedBuildInPropertyCount && id <= string16Count + this->expectedBuildInPropertyCount))
        {
            Assert(false);
        }
        const unaligned StringIndexRecord* record = string16IndexTable + (id - this->expectedBuildInPropertyCount);
        if(isPropertyRecord)
        {
            *isPropertyRecord = record->isPropertyRecord;
        }
        auto offset = record->offset;
        auto addressOfString = raw + offset;
        return (LPCWSTR)addressOfString;
    }

    uint32 GetString16LengthById(int id)
    {
        if(!(id >= this->expectedBuildInPropertyCount && id<=string16Count + this->expectedBuildInPropertyCount))
        {
            Assert(false);
        }
        LPCWSTR s1 = GetString16ById(id);
        LPCWSTR s2 = GetString16ById(id + 1);
        auto result = s2 - s1 - 1;
        Assert(result <= UINT_MAX);
        return (uint32)result;
    }

    HRESULT ReadHeader()
    {
        auto current = ReadConstantSizedInt32NoSize(raw, &magic);
        if (magic != magicConstant)
        {
            AssertMsg(false, "Unrecognized magic constant in byte code file header. Is this really a bytecode file?");
            return E_FAIL;
        }
        current = ReadConstantSizedInt32NoSize(current, &totalSize);
        current = ReadByte(current, &fileVersionScheme);

        byte expectedFileVersionScheme = isLibraryCode? ReleaseVersioningScheme : CurrentFileVersionScheme;
#if ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema)
        {
            expectedFileVersionScheme = (byte)Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema;
        }
#endif
        // Ignore the version scheme check if it is library code
        if (!isLibraryCode && fileVersionScheme != expectedFileVersionScheme)
        {
            // File version scheme is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }

        DWORD expectedV1 = 0;
        DWORD expectedV2 = 0;
        DWORD expectedV3 = 0;
        DWORD expectedV4 = 0;

        if (expectedFileVersionScheme != ReleaseVersioningScheme)
        {
            Js::VerifyCatastrophic(!isLibraryCode);
            Js::VerifyCatastrophic(expectedFileVersionScheme == EngineeringVersioningScheme);
            Js::VerifyOkCatastrophic(AutoSystemInfo::GetJscriptFileVersion(&expectedV1, &expectedV2, &expectedV3, &expectedV4));
        }
        else
        {
            auto guidDWORDs = (DWORD*)(&byteCodeCacheReleaseFileVersion);
            expectedV1 = guidDWORDs[0];
            expectedV2 = guidDWORDs[1];
            expectedV3 = guidDWORDs[2];
            expectedV4 = guidDWORDs[3];
        }
#if ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.ForceSerializedBytecodeMajorVersion)
        {
            expectedV1 = Js::Configuration::Global.flags.ForceSerializedBytecodeMajorVersion;
            expectedV2 = 0;
            expectedV3 = 0;
            expectedV4 = 0;
        }
#endif
        current = ReadConstantSizedInt32(current, &V1);
        if ((DWORD)V1!=expectedV1)
        {
            // Incompatible major version
            return ByteCodeSerializer::InvalidByteCode;
        }
        // Library code is serialized with one build of the engine and then included into a subsequent build, so can't have match
        // on the build timestamp hash. Also want to share the generated bytecode between x86/ARM and debug/release, so skip the extra
        // checking. Will rework this validation entirely under TFS 555060
        current = ReadConstantSizedInt32(current, &V2);
        if ((DWORD)V2 != expectedV2)
        {
            // Incompatible minor version
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadConstantSizedInt32(current, &V3);
        if ((DWORD)V3 != expectedV3)
        {
            // Incompatible 3rd version part
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadConstantSizedInt32(current, &V4);
        if ((DWORD)V4 != expectedV4)
        {
            // Incompatible 4th version part
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadByte(current, &architecture);
        if (architecture!=magicArchitecture)
        {
            // This byte cache file was created with against a chakra running under a different architecture. It is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }

        int functionBodySize, buildInPropertyCount, opCodeCount;
        current = ReadInt32(current, &functionBodySize);
        if (functionBodySize != expectedFunctionBodySize)
        {
            // The size of function body didn't match. It is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadInt32(current, &buildInPropertyCount);
        if (buildInPropertyCount!=expectedBuildInPropertyCount)
        {
            // This byte cache file was created with against a chakra that has a different number of built in properties. It is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadInt32(current, &opCodeCount);
        if (opCodeCount != expectedOpCodeCount)
        {
            // This byte cache file was created with against a chakra that has a different number of built in properties. It is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadInt32(current, &sourceSize);
        current = ReadInt32(current, &sourceCharLength);

        current = ReadOffsetAsPointer(current, &string16s);
        current = ReadOffsetAsPointer(current, &lineInfoCaches);
        current = ReadOffsetAsPointer(current, &sourceSpans);
        current = ReadOffsetAsPointer(current, &functions);

        // Read strings header
        string16IndexTable = (StringIndexRecord*)ReadInt32(string16s, &string16Count);
        lineInfoCache = (JsUtil::LineOffsetCache<Recycler>::LineOffsetCacheItem *)ReadInt32(lineInfoCaches, &lineInfoCacheCount);

        string16Table = (byte*)(string16IndexTable + string16Count + 1);

        // string16Table is aligned to 2-bytes
        uint32 string16TableOffset = (uint32)(string16Table - raw);
        string16TableOffset = ::Math::Align(string16TableOffset, (uint32)sizeof(wchar_t));
        string16Table = raw + string16TableOffset;

        return S_OK;
    }

    const byte* ReadStringConstant(const byte* current, FunctionBody* function, LPCWSTR& string, uint32& len)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartStringConstant);
#endif
        int stringId;
        current = ReadInt32(current, &stringId);
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndStringConstant);
#endif
        string = GetString16ById(stringId);
        len = GetString16LengthById(stringId);

        return current;
    }

    const byte* ReadStringTemplateCallsiteConstant(const byte* current, FunctionBody* function, Var& var)
    {
        int arrayLength = 0;
        current = ReadInt32(current, &arrayLength);

        ScriptContext* scriptContext = function->GetScriptContext();

        ENTER_PINNED_SCOPE(Js::JavascriptArray, callsite);
        callsite = scriptContext->GetLibrary()->CreateArray(arrayLength);

        LPCWSTR string;
        uint32 len;
        uint32 rawlen = 0;

        for (int i = 0; i < arrayLength; i++)
        {
            current = ReadStringConstant(current, function, string, len);

            JavascriptString* str = JavascriptString::NewCopyBuffer(string, len, scriptContext);
            callsite->SetItemWithAttributes(i, str, PropertyEnumerable);
        }

        JavascriptArray* rawArray = scriptContext->GetLibrary()->CreateArray(arrayLength);

        for (int i = 0; i < arrayLength; i++)
        {
            current = ReadStringConstant(current, function, string, len);
            rawlen += len;

            JavascriptString* str = JavascriptString::NewCopyBuffer(string, len, scriptContext);
            rawArray->SetItemWithAttributes(i, str, PropertyEnumerable);
        }

        rawArray->Freeze();
        callsite->SetPropertyWithAttributes(Js::PropertyIds::raw, rawArray, PropertyNone, nullptr);
        callsite->Freeze();

        JavascriptLibrary* library = scriptContext->GetLibrary();

        var = library->TryGetStringTemplateCallsiteObject(callsite);

        if (var == nullptr)
        {
            library->AddStringTemplateCallsiteObject(callsite);
            var = callsite;
        }

        LEAVE_PINNED_SCOPE();

        return current;
    }

#ifndef TEMP_DISABLE_ASMJS
    const byte * ReadAsmJsConstantsTable(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfConstantTable);
#endif

        function->CreateConstantTable();

        uint32 intConstCount = function->GetAsmJsFunctionInfo()->GetIntConstCount();
        uint32 floatConstCount = function->GetAsmJsFunctionInfo()->GetFloatConstCount();
        uint32 doubleConstCount = function->GetAsmJsFunctionInfo()->GetDoubleConstCount();
        Js::Var * constTable = static_cast<Js::Var *>(function->GetConstTable());

        int * intConstTable = reinterpret_cast<int *>(constTable + Js::AsmJsFunctionMemory::RequiredVarConstants - 1);
        for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < intConstCount; ++reg)
        {
            current = ReadConstantSizedInt32(current, &intConstTable[reg]);
        }

        float * floatConstTable = reinterpret_cast<float *>(intConstTable + intConstCount);

        for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < floatConstCount; ++reg)
        {
            current = ReadFloat(current, &floatConstTable[reg]);
        }

        double * doubleConstTable = reinterpret_cast<double *>(floatConstTable + floatConstCount);

        for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < doubleConstCount; ++reg)
        {
            current = ReadDouble(current, &doubleConstTable[reg]);
        }

        uint32 simdConstCount = function->GetAsmJsFunctionInfo()->GetSimdConstCount();
        AsmJsSIMDValue *simdConstTable = reinterpret_cast<AsmJsSIMDValue *>(doubleConstTable + doubleConstCount);

        for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < simdConstCount; ++reg)
        {
            current = ReadSIMDValue(current, &simdConstTable[reg]);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfConstantTable);
#endif

        return current;
    }
#endif

    const byte * ReadConstantsTable(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfConstantTable);
#endif

        function->CreateConstantTable();

        for (auto reg = FunctionBody::FirstRegSlot + 1; reg < function->m_constCount; reg++) // Ignore first slot, it is always global or module root and has been preinitialized.
        {
            byte ct;
            current = ReadByte(current, &ct);
            switch(ct)
            {
            case ctString16:
                {
                    LPCWSTR string;
                    uint32 len;
                    current = ReadStringConstant(current, function, string, len);

                    function->RecordStrConstant(reg, string, len);
                    break;
                }
            case ctStringTemplateCallsite:
                {
                    Var callsite;
                    current = ReadStringTemplateCallsiteConstant(current, function, callsite);

                    function->RecordConstant(reg, callsite);
                    break;
                }
            case ctInt:
                {
                    int value;
                    current = ReadConstantSizedInt32(current, &value);
                    function->RecordIntConstant(reg, value);
                    break;
                }
            case ctNull:
                function->RecordNullObject(reg);
                break;
            case ctUndefined:
                function->RecordUndefinedObject(reg);
                break;
            case ctNumber:
                {
                    double value;
                    current = ReadDouble(current, &value);
                    function->RecordFloatConstant(reg, value);
                    break;
                }
            case ctNullDisplay:
                function->RecordNullDisplayConstant(reg);
                break;
            case ctStrictNullDisplay:
                function->RecordStrictNullDisplayConstant(reg);
                break;
            case ctTrue:
                function->RecordTrueObject(reg);
                break;
            case ctFalse:
                function->RecordFalseObject(reg);
                break;
            default:
                AssertMsg(UNREACHED, "Unexpected object type in ReadConstantsTable");
                break;
            }
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfConstantTable);
#endif

        return current;
    }

    const byte * ReadPropertyIdsForScopeSlotArray(const byte * current, FunctionBody * function)
    {
        if (function->scopeSlotArraySize == 0)
        {
            return current;
        }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfPropertyIdsForScopeSlotArray);
#endif

        function->SetPropertyIdsForScopeSlotArray(RecyclerNewArrayLeaf(scriptContext->GetRecycler(), Js::PropertyId, function->scopeSlotArraySize), function->scopeSlotArraySize);

        for (uint i = 0; i < function->scopeSlotArraySize; i++)
        {
            int value;
            current = ReadInt32(current, &value);
            PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(value);
            function->GetPropertyIdsForScopeSlotArray()[i] =  propertyId;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfPropertyIdsForScopeSlotArray);
#endif

        return current;
    }

    const byte * ReadSlotArrayDebuggerScopeProperties(const byte * current, FunctionBody* function, DebuggerScope* debuggerScope, uint propertyCount)
    {
        Assert(function);
        Assert(debuggerScope);
        if (propertyCount == 0)
        {
            return current;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfDebuggerScopeProperties);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        for (uint i = 0u; i < propertyCount; ++i)
        {
            // Read the slot array index and property ID for each property (for heap enum to use).  The remaining properties
            // are needed for the debugger and will be filled in when generating byte code.
            int value;
            current = ReadInt32(current, &value);
            RegSlot slotIndex = value;

            current = ReadInt32(current, &value);
            PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(value);
            debuggerScope->AddProperty(slotIndex, propertyId, DebuggerScopePropertyFlags_None);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfDebuggerScopeProperties);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        return current;
    }

    const byte * ReadSlotArrayDebuggerScopes(const byte * current, FunctionBody * function, uint debuggerScopeCount)
    {
        Assert(function);
        if (debuggerScopeCount == 0)
        {
            return current;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfDebuggerScopes);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        AssertMsg(function->GetScopeObjectChain() == nullptr, "Scope chain should not exist before deserialization.");
        function->SetScopeObjectChain(RecyclerNew(scriptContext->GetRecycler(), ScopeObjectChain, scriptContext->GetRecycler()));

        int currentScopeOffset = 0;
        for (uint i = 0u; i < debuggerScopeCount; i++)
        {
            int scopeIndex;
            current = ReadInt32(current, &scopeIndex);
            DebuggerScope* slotArrayDebuggerScope = nullptr;
            AssertMsg(currentScopeOffset <= scopeIndex, "Scope indices were not inserted into the serialized byte code in ascending order.");
            while (currentScopeOffset <= scopeIndex)
            {
                // Fill the chain with dummy scopes until we reach the slot array scope we're on.
                // These non-slot array scopes are only needed for the debugger and will be filled in
                // properly during byte code generation (when attaching).
                // We also don't need to worry about the parenting/sibling chain, as this will be built when
                // generating bytecode as well.
                slotArrayDebuggerScope = function->AddScopeObject(Js::DiagUnknownScope, 0, Constants::NoRegister);
                ++currentScopeOffset;
            }

            Assert(slotArrayDebuggerScope);

            // Create the slot array properties.
            int propertyCount;
            current = ReadInt32(current, &propertyCount);
            current = ReadSlotArrayDebuggerScopeProperties(current, function, slotArrayDebuggerScope, propertyCount);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfDebuggerScopes);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        return current;
    }

    const byte * ReadCacheIdToPropertyIdMap(const byte * current, FunctionBody * function)
    {
        uint count = function->GetInlineCacheCount();
        if (count == 0)
        {
            return current;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfCacheIdToPropIdMap);
#endif

        function->CreateCacheIdToPropertyIdMap();

        for (uint i = 0; i < count; i++)
        {
            int value;
            current = ReadInt32(current, &value);
            PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(value);
            function->SetPropertyIdForCacheId(i, propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfCacheIdToPropIdMap);
#endif
#if DBG
        function->VerifyCacheIdToPropertyIdMap();
#endif
        return current;
    }

    const byte * ReadReferencedPropertyIdMap(const byte * current, FunctionBody * function)
    {
        uint count = function->GetReferencedPropertyIdCount();
        if (count == 0)
        {
            return current;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfReferencedPropIdMap);
#endif

        function->CreateReferencedPropertyIdMap();

        for (uint i = 0; i < count; i++)
        {
            int value;
            current = ReadInt32(current, &value);
            PropertyId propertyId = function->GetByteCodeCache()->LookupNonBuiltinPropertyId(value);
            function->SetReferencedPropertyIdWithMapIndex(i, propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfReferencedPropIdMap);
#endif
#if DBG
        function->VerifyReferencedPropertyIdMap();
#endif
        return current;
    }

    // Read a growing Uint32 array
    const byte * ReadGrowingUint32Array(const byte * current, JsUtil::GrowingUint32HeapArray ** arr)
    {
        int count = 0;
        current = ReadInt32(current, &count);
        if (count == 0)
        {
           (*arr) = nullptr;
           return current;
        }
        (*arr) = JsUtil::GrowingUint32HeapArray::Create(/*length=*/count);
        js_memcpy_s((*arr)->GetBuffer(), count * sizeof(uint32), current, count*sizeof(uint32));
        (*arr)->SetCount(count);
        current += count * sizeof(uint32);
        return current;
    }

    // Read a small span sequence
    const byte * ReadSmallSpanSequence(const byte * current, SmallSpanSequence ** smallSpanSequence)
    {
        (*smallSpanSequence) = HeapNew(SmallSpanSequence);
        current = ReadInt32(current, &(*smallSpanSequence)->baseValue);
        current = ReadGrowingUint32Array(current, &(*smallSpanSequence)->pStatementBuffer); // CONSIDER: It would be really nice to change GrowingUint32Array to something with a fixed, readonly layout
        current = ReadGrowingUint32Array(current, &(*smallSpanSequence)->pActualOffsetList);
        return current;
    }

    void ReadSourceInfo(const byte * functionBytes, int& lineNumber, int& columnNumber, bool& m_isEval, bool& m_isDynamicFunction)
    {
        int displayNameId;
        unsigned int bitflags;
        this->ReadFunctionBodyHeader(functionBytes, displayNameId, lineNumber, columnNumber, bitflags);
        m_isEval = (bitflags & ffIsEval) ? true : false;
        m_isDynamicFunction = (bitflags & ffIsDynamicFunction) ? true : false;
    }

    const byte * ReadFunctionBodyHeader(const byte * functionBytes, int& displayNameId, int& lineNumber, int& columnNumber, unsigned int& bitflags)
    {
        serialization_alignment SerializedFieldList* definedFields = (serialization_alignment SerializedFieldList*) functionBytes;

        // Basic function body constructor arguments
        const byte * current = functionBytes + sizeof(serialization_alignment SerializedFieldList);
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfFunctionBody);
#endif
        current = ReadInt32(current, &displayNameId);
        if (definedFields->has_m_lineNumber)
        {
            current = ReadInt32(current, &lineNumber);
        }
        else
        {
            lineNumber = 0;
        }

        if (definedFields->has_m_columnNumber)
        {
            current = ReadInt32(current, &columnNumber);
        }
        else
        {
            columnNumber = 0;
        }

        current = ReadUInt32(current, &bitflags);
        return current;
    }

#ifndef TEMP_DISABLE_ASMJS
    const byte * ReadAsmJsFunctionInfo(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfAsmJsFuncInfo);
#endif

        AsmJsFunctionInfo* funcInfo = function->AllocateAsmJsFunctionInfo();

        int retVal;
        current = ReadInt32(current, &retVal);
        funcInfo->SetReturnType(AsmJsRetType((AsmJsRetType::Which)retVal));

        int count;
        current = ReadInt32(current, &count);
        funcInfo->SetIntConstCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetDoubleConstCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetFloatConstCount(count);

        ArgSlot argCount;
        current = ReadUInt16(current, &argCount);
        funcInfo->SetArgCount(argCount);

        ArgSlot argByteSize;
        current = ReadUInt16(current, &argByteSize);
        funcInfo->SetArgByteSize(argByteSize);

        current = ReadInt32(current, &count);
        funcInfo->SetIntVarCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetDoubleVarCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetFloatVarCount(count);

        current = ReadInt32(current, &count);
        funcInfo->SetIntTmpCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetDoubleTmpCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetFloatTmpCount(count);

        ArgSlot argSizeArrayLength;
        current = ReadUInt16(current, &argSizeArrayLength);
        funcInfo->SetArgSizeArrayLength(argSizeArrayLength);
        uint* argArray = RecyclerNewArrayLeafZ(scriptContext->GetRecycler(), uint, argSizeArrayLength);
        funcInfo->SetArgsSizesArray(argArray);
        for (int i = 0; i < argSizeArrayLength; i++)
        {
            int32 size;
            current = ReadConstantSizedInt32(current, &size);
            argArray[i] = (uint32)size;
        }

        if (argCount > 0)
        {
            AsmJsVarType::Which * typeArray = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), AsmJsVarType::Which, argCount);
            funcInfo->SetArgTypeArray(typeArray);
            for (uint i = 0; i < argCount; i++)
            {
                current = ReadByte(current, (byte*)&typeArray[i]);
            }
        }

        current = ReadInt32(current, &count);
        funcInfo->SetIntByteOffset(count);
        current = ReadInt32(current, &count);
        funcInfo->SetDoubleByteOffset(count);
        current = ReadInt32(current, &count);
        funcInfo->SetFloatByteOffset(count);

        bool boolVal;
        current = ReadBool(current, &boolVal);
        funcInfo->SetIsHeapBufferConst(boolVal);
        current = ReadBool(current, &boolVal);
        funcInfo->SetUsesHeapBuffer(boolVal);

        current = ReadInt32(current, &count);
        funcInfo->SetSimdConstCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetSimdVarCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetSimdTmpCount(count);
        current = ReadInt32(current, &count);
        funcInfo->SetSimdByteOffset(count);

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfAsmJsFuncInfo);
#endif

        return current;
    }

    const byte * ReadAsmJsModuleInfo(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfAsmJsModuleInfo);
#endif
        AsmJsModuleInfo* moduleInfo = function->AllocateAsmJsModuleInfo();

        int count;
        current = ReadInt32(current, &count);
        moduleInfo->SetArgInCount(count);

        int exportsCount;
        current = ReadInt32(current, &exportsCount);
        moduleInfo->SetExportsCount(exportsCount);

        current = ReadInt32(current, &count);
        moduleInfo->InitializeSlotMap(count);

        current = ReadInt32(current, &count);
        moduleInfo->SetSimdRegCount(count);

        int id;
        if (exportsCount > 0)
        {
            PropertyIdArray * propArray = moduleInfo->GetExportsIdArray();

            bool boolVal;
            current = ReadBool(current, &boolVal);
            propArray->hadDuplicates = boolVal;

            current = ReadBool(current, &boolVal);
            propArray->has__proto__ = boolVal;

            current = ReadInt32(current, &count);
            propArray->count = count;

            for (uint i = 0; i < propArray->count; i++)
            {
                current = ReadInt32(current, &id);
                PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(id);
                propArray->elements[i] = propertyId;
            }

            RegSlot* exportLocations = moduleInfo->GetExportsFunctionLocation();
            for (int i = 0; i < exportsCount; i++)
            {
                int32 loc;
                current = ReadConstantSizedInt32(current, &loc);
                exportLocations[i] = (uint32)loc;
            }
        }
        RegSlot regSlot;
        current = ReadUInt32(current, &regSlot);
        moduleInfo->SetExportFunctionIndex(regSlot);

        current = ReadInt32(current, &count);
        moduleInfo->SetVarCount(count);

        for (int i = 0; i < count; i++)
        {
            serialization_alignment AsmJsModuleInfo::ModuleVar * modVar = (serialization_alignment AsmJsModuleInfo::ModuleVar*)current;
            moduleInfo->SetVar(i, *modVar);
            current = current + sizeof(serialization_alignment AsmJsModuleInfo::ModuleVar);
        }

        current = ReadInt32(current, &count);
        moduleInfo->SetVarImportCount(count);
        AsmJsModuleInfo::ModuleVarImport varImport;
        for (int i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &varImport.location);
            current = ReadByte(current, (byte*)&varImport.type);
            current = ReadInt32(current, &id);
            varImport.field = function->GetByteCodeCache()->LookupPropertyId(id);
            moduleInfo->SetVarImport(i, varImport);
        }


        current = ReadInt32(current, &count);
        moduleInfo->SetFunctionImportCount(count);
        AsmJsModuleInfo::ModuleFunctionImport funcImport;
        for (int i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &funcImport.location);
            current = ReadInt32(current, &id);
            funcImport.field = function->GetByteCodeCache()->LookupPropertyId(id);
            moduleInfo->SetFunctionImport(i, funcImport);
        }

        current = ReadInt32(current, &count);
        moduleInfo->SetFunctionCount(count);

        AsmJsModuleInfo::ModuleFunction modFunc;
        for (int i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &modFunc.location);
            moduleInfo->SetFunction(i, modFunc);
        }

        current = ReadInt32(current, &count);
        moduleInfo->SetFunctionTableCount(count);

        AsmJsModuleInfo::ModuleFunctionTable funcTable;
        for (int i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &funcTable.size);
            if (funcTable.size > 0)
            {
                funcTable.moduleFunctionIndex = RecyclerNewArray(this->scriptContext->GetRecycler(), RegSlot, funcTable.size);
            }
            else
            {
                funcTable.moduleFunctionIndex = nullptr;
            }
            for (uint j = 0; j < funcTable.size; j++)
            {
                current = ReadConstantSizedInt32(current, (int32*)&funcTable.moduleFunctionIndex[j]);
            }
            moduleInfo->SetFunctionTable(i, funcTable);
        }

        serialization_alignment AsmJsModuleMemory * modMem = (serialization_alignment AsmJsModuleMemory*)current;
        moduleInfo->SetModuleMemory(*modMem);
        current = current + sizeof(serialization_alignment AsmJsModuleMemory);

        current = ReadInt32(current, &count);
        for (int i = 0; i < count; i++)
        {

            current = ReadInt32(current, &id);
            PropertyId key = function->GetByteCodeCache()->LookupPropertyId(id);

            serialization_alignment AsmJsSlot * slot = (serialization_alignment AsmJsSlot*)current;
            current = current + sizeof(serialization_alignment AsmJsSlot);

            // copy the slot to recycler memory
            AsmJsSlot * recyclerSlot = RecyclerNew(scriptContext->GetRecycler(), AsmJsSlot);
            *recyclerSlot = *slot;
            moduleInfo->GetAsmJsSlotMap()->Add(key, recyclerSlot);
        }

        serialization_alignment BVStatic<ASMMATH_BUILTIN_SIZE> * mathBV = (serialization_alignment BVStatic<ASMMATH_BUILTIN_SIZE>*)current;
        current = current + sizeof(serialization_alignment BVStatic<ASMMATH_BUILTIN_SIZE>);
        moduleInfo->SetAsmMathBuiltinUsed(*mathBV);

        serialization_alignment BVStatic<ASMARRAY_BUILTIN_SIZE> * arrayBV = (serialization_alignment BVStatic<ASMARRAY_BUILTIN_SIZE>*)current;
        current = current + sizeof(serialization_alignment BVStatic<ASMARRAY_BUILTIN_SIZE>);
        moduleInfo->SetAsmArrayBuiltinUsed(*arrayBV);

        serialization_alignment BVStatic<ASMSIMD_BUILTIN_SIZE> * simdBV = (serialization_alignment BVStatic<ASMSIMD_BUILTIN_SIZE>*)current;
        current = current + sizeof(serialization_alignment BVStatic<ASMSIMD_BUILTIN_SIZE>);
        moduleInfo->SetAsmSimdBuiltinUsed(*simdBV);
        uint maxAccess;
        current = ReadUInt32(current, &maxAccess);
        moduleInfo->SetMaxHeapAccess(maxAccess);

        bool usesChangeHeap;
        current = ReadBool(current, &usesChangeHeap);
        moduleInfo->SetUsesChangeHeap(usesChangeHeap);

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfAsmJsModuleInfo);
#endif

        return current;
    }
#endif

    // Read a function body
    HRESULT ReadFunctionBody(const byte * functionBytes, FunctionProxy ** functionProxy, Utf8SourceInfo* sourceInfo, ByteCodeCache * cache, NativeModule *nativeModule, bool deserializeThis, bool deserializeNested = true, Js::DeferDeserializeFunctionInfo* deferDeserializeFunctionInfo = NULL)
    {
        Assert(sourceInfo->GetSrcInfo()->moduleID == kmodGlobal);

        int displayNameId;
        int lineNumber;
        int columnNumber;
        unsigned int bitflags;
        const byte * current = this->ReadFunctionBodyHeader(functionBytes, displayNameId, lineNumber, columnNumber, bitflags);

        serialization_alignment SerializedFieldList* definedFields = (serialization_alignment SerializedFieldList*) functionBytes;

        auto displayName = deferDeserializeFunctionInfo != nullptr ?
            deferDeserializeFunctionInfo->GetDisplayName() :
            GetString16ById(displayNameId);

        uint displayNameLength = deferDeserializeFunctionInfo ? deferDeserializeFunctionInfo->GetDisplayNameLength() : GetString16LengthById(displayNameId);
        uint displayShortNameOffset = deferDeserializeFunctionInfo ? deferDeserializeFunctionInfo->GetShortDisplayNameOffset() : 0;
        int functionId;
        current = ReadInt32(current, &functionId);
        int serializationIndex;
        current = ReadInt32(current, &serializationIndex);
        int32 attributes;
        current = ReadInt32(current, &attributes);

        uint32 offsetIntoSource = 0;
        current = ReadUInt32(current, &offsetIntoSource);

        int nestedCount = 0;
        if (definedFields->has_m_nestedCount)
        {
            current = ReadInt32(current, &nestedCount);
        }

        if (!deserializeThis)
        {
            Assert(sourceInfo->GetSrcInfo()->moduleID == kmodGlobal);
            Assert(!deserializeNested);
            *functionProxy = DeferDeserializeFunctionInfo::New(this->scriptContext, nestedCount, functionId, cache, functionBytes, sourceInfo, displayName, displayNameLength, displayShortNameOffset, nativeModule, (FunctionInfo::Attributes)attributes);

            return S_OK;
        }

        ParseableFunctionInfo **function = (ParseableFunctionInfo **) functionProxy;
        uint functionNumber;

        if (deferDeserializeFunctionInfo)
        {
            functionNumber = deferDeserializeFunctionInfo->GetFunctionNumber();
        }
        else
        {
            functionNumber = scriptContext->GetThreadContext()->NewFunctionNumber();
        }

        if (definedFields->has_m_constCount)
        {
            FunctionBody **functionBody = (FunctionBody **) function;

            *functionBody = FunctionBody::NewFromRecycler(this->scriptContext, nullptr /*displayName*/, 0 /*displayNameLength*/, 0 /*displayShortNameOffset*/, nestedCount,
                sourceInfo,
                functionNumber,
                sourceInfo->GetSrcInfo()->sourceContextInfo->sourceContextId,
                firstFunctionId + functionId, nullptr, (FunctionInfo::Attributes)attributes
#ifdef PERF_COUNTERS
                , (deferDeserializeFunctionInfo != nullptr)
#endif
                );

            (*functionBody)->SetDisplayName(displayName, displayNameLength, displayShortNameOffset, FunctionProxy::SetDisplayNameFlags::SetDisplayNameFlagsDontCopy);
            (*functionBody)->serializationIndex = serializationIndex;
            (*functionBody)->SetByteCodeCache(cache);
            (*functionBody)->SetUtf8SourceInfo(utf8SourceInfo); // Set source info
            (*function)->m_utf8SourceHasBeenSet = true;
        }
        else
        {
            *function = ParseableFunctionInfo::New(this->scriptContext, nestedCount, firstFunctionId + functionId, utf8SourceInfo, displayName, displayNameLength, displayShortNameOffset, nullptr, (FunctionInfo::Attributes)attributes);
        }

        // These fields are manually deserialized previously
        (*function)->m_lineNumber = lineNumber;
        (*function)->m_columnNumber = columnNumber;
        (*function)->m_isDeclaration = (bitflags & ffIsDeclaration) ? true : false;
        (*function)->m_hasImplicitArgIns = (bitflags & ffHasImplicitArgsIn) ? true : false;
        (*function)->m_isAccessor = (bitflags & ffIsAccessor) ? true : false;
        (*function)->m_isStaticNameFunction = (bitflags & ffIsStaticNameFunction) ? true : false;
        (*function)->m_isNamedFunctionExpression = (bitflags & ffIsNamedFunctionExpression) ? true : false;
        (*function)->m_isNameIdentifierRef  = (bitflags & ffIsNameIdentifierRef ) ? true : false;
        (*function)->m_isGlobalFunc = (bitflags & ffIsGlobalFunc) ? true : false;
        (*function)->m_dontInline = (bitflags & ffDontInline) ? true : false;
        (*function)->m_isStrictMode = (bitflags & ffIsStrictMode) ? true : false;
        (*function)->m_doBackendArgumentsOptimization = (bitflags & ffDoBackendArgumentsOptimization) ? true : false;
        (*function)->m_isEval = (bitflags & ffIsEval) ? true : false;
        (*function)->m_isDynamicFunction = (bitflags & ffIsDynamicFunction) ? true : false;

        // This is offsetIntoSource is the start offset in bytes as well.
        (*function)->m_cbStartOffset = (size_t) offsetIntoSource;
        (*function)->m_sourceIndex = this->sourceIndex;

#define DEFINE_FUNCTION_PROXY_FIELDS 1
#define DEFINE_PARSEABLE_FUNCTION_INFO_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
        if (definedFields->has_##name == true) { \
            current = Read##serializableType(current, &(*function)->##name); \
        }
#define DECLARE_MANUAL_SERIALIZABLE_FIELD(type, name, serializableType, deserializeHere) \
        if (deserializeHere && definedFields->has_##name == true) { \
            current = Read##serializableType(current, &(*function)->##name); \
        }
#include "SerializableFunctionFields.h"

        if (definedFields->has_m_constCount)
        {
            FunctionBody **functionBody = (FunctionBody **)function;

#define DEFINE_FUNCTION_BODY_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
            if (definedFields->has_##name == true) { \
                current = Read##serializableType(current, &(*functionBody)->##name); \
            }
#define DECLARE_MANUAL_SERIALIZABLE_FIELD(type, name, serializableType, deserializeHere) \
            if (deserializeHere && definedFields->has_##name == true) { \
                current = Read##serializableType(current, &(*functionBody)->##name); \
            }
#include "SerializableFunctionFields.h"

            // TODO-STACK-NESTED-FUNC: Defer deserialize function doesn't have parent pointer, can't do stack nested func yet
            // The flags field is set to by default to Flags_HasNoExplicitReturnValue which means if it's serialized, the field will be set
            // in the definedFields struct. If it's not set, that means that the flag was explicitly set to Flags_None so we'll have to set
            // that here.
            if (definedFields->has_flags == false)
            {
                (*functionBody)->flags = FunctionBody::FunctionBodyFlags::Flags_None;
            }
            else
            {
                (*functionBody)->flags = (FunctionBody::FunctionBodyFlags)((*functionBody)->flags & ~FunctionBody::Flags_StackNestedFunc);
            }

            if (definedFields->has_m_firstTmpReg == false)
            {
                (*functionBody)->m_firstTmpReg = 0;
            }

            if (definedFields->has_m_envDepth == false)
            {
                (*functionBody)->m_envDepth = 0;
            }

            if (deserializeThis && !deserializeNested)
            {
                (*functionBody)->m_isPartialDeserializedFunction = true;
            }
            (*functionBody)->FinishSourceInfo(); // SourceInfo is complete. Register this functionBody to utf8SourceInfo.
            (*functionBody)->m_isFuncRegistered = (bitflags & ffIsFuncRegistered) ? true : false;
            (*functionBody)->m_hasAllNonLocalReferenced = (bitflags & ffhasAllNonLocalReferenced) ? true : false;
            (*functionBody)->m_hasSetIsObject = (bitflags & ffhasSetIsObject) ? true : false;
            (*functionBody)->m_CallsEval = (bitflags & ffhasSetCallsEval) ? true : false;
            (*functionBody)->m_ChildCallsEval = (bitflags & ffChildCallsEval) ? true : false;
            (*functionBody)->m_hasReferenceableBuiltInArguments = (bitflags & ffHasReferenceableBuiltInArguments) ? true : false;
#ifndef TEMP_DISABLE_ASMJS
            (*functionBody)->m_isAsmJsFunction = (bitflags & ffIsAsmJsFunction) ? true : false;
            (*functionBody)->m_isAsmjsMode = (bitflags & ffIsAsmJsMode) ? true : false;
#endif

            byte loopHeaderExists;
            current = ReadByte(current, &loopHeaderExists);
            if (loopHeaderExists)
            {
                (*functionBody)->AllocateLoopHeaders();
                auto loopHeaderArray = (*functionBody)->GetLoopHeaderArray();
                for (uint i = 0; i < (*functionBody)->loopCount; ++i)
                {
                    uint startOffset, endOffset;
                    current = ReadUInt32(current, &startOffset);
                    current = ReadUInt32(current, &endOffset);
                    loopHeaderArray[i].startOffset = startOffset;
                    loopHeaderArray[i].endOffset = endOffset;
                }
            }

            byte asmJsInfoExists;
            current = ReadByte(current, &asmJsInfoExists);
#ifndef TEMP_DISABLE_ASMJS
            if (asmJsInfoExists == 1)
            {
                current = ReadAsmJsFunctionInfo(current, *functionBody);
            }
            else if (asmJsInfoExists == 2)
            {
                current = ReadAsmJsModuleInfo(current, *functionBody);
            }
            else
#endif
            {
                Assert(asmJsInfoExists == 0);
            }

            // Read constants table
#ifndef TEMP_DISABLE_ASMJS
            if ((*functionBody)->GetIsAsmJsFunction())
            {
                current = ReadAsmJsConstantsTable(current, *functionBody);
            }
            else
#endif
            {
                current = ReadConstantsTable(current, *functionBody);
            }

            // Byte code
            current = ReadByteBlock(current, &(*functionBody)->byteCodeBlock);

            // Auxiliary
            current = ReadAuxiliary(current, *functionBody);


            // Inline cache
            current = ReadCacheIdToPropertyIdMap(current, *functionBody);
            current = ReadReferencedPropertyIdMap(current, *functionBody);
            (*functionBody)->AllocateInlineCache();

            current = ReadPropertyIdsForScopeSlotArray(current, *functionBody);

            uint debuggerScopeCount = 0;
            current = ReadUInt32(current, &debuggerScopeCount);
            current = ReadSlotArrayDebuggerScopes(current, *functionBody, debuggerScopeCount);

            (*functionBody)->AllocateObjectLiteralTypeArray();

            // Literal regexes
            (*functionBody)->AllocateLiteralRegexArray();
            for (uint i = 0; i < (*functionBody)->literalRegexCount; ++i)
            {
                int length;
                current = ReadInt32(current, &length);
                if (length == -1)
                {
                    Assert(!(*functionBody)->GetLiteralRegex(i));
                    continue;
                }

                int sourceId;
                current = ReadInt32(current, &sourceId);
                const auto source = GetString16ById(sourceId);

                UnifiedRegex::RegexFlags flags;
                CompileAssert(sizeof(flags) == sizeof(byte));
                current = ReadByte(current, reinterpret_cast<byte *>(&flags));
                (*functionBody)->SetLiteralRegex(i, RegexHelper::CompileDynamic(scriptContext, source, length, flags, true));
            }

            // Read source information
            current = ReadSmallSpanSequence(current, &(*functionBody)->m_sourceInfo.pSpanSequence);

            (*functionBody)->InitializeExecutionModeAndLimits();
        }

        // Read lexically nested functions
        if (nestedCount)
        {
            for(auto i = 0; i<nestedCount; ++i)
            {
                const byte * nestedFunctionBytes;
                current = ReadOffsetAsPointer(current, &nestedFunctionBytes);
                if (nestedFunctionBytes == nullptr)
                {
                    (*function)->SetNestedFunc(NULL, i, 0u);
                }
                else
                {
                    FunctionProxy* nestedFunction;

                    // If we should deserialize nested functions, go ahead and do so
                    // If we shouldn't, and we're currently deserializing a function proxy
                    // that has been defer-deserialized, simply copy over the function proxy from
                    // from the old function- otherwise, create proxies for the nested functions
                    auto hr = ReadFunctionBody(nestedFunctionBytes, &nestedFunction, sourceInfo, cache, nativeModule, deserializeNested, deserializeNested);
                    if (FAILED(hr))
                    {
                        Assert(0);
                        return hr;
                    }

                    (*function)->SetNestedFunc(nestedFunction, i, 0u);
                }
            }
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        // Magical ending
        int constant;
        ReadInt32(current, &constant);
        if (constant != magicEndOfFunctionBody)
        {
            Assert(constant == magicEndOfFunctionBody);
            Throw::FatalInternalError();
        }
#endif
        if (definedFields->has_m_constCount)
        {
            FunctionBody **functionBody = (FunctionBody **) function;
#if DBG
            if (PHASE_DUMP(Js::DebuggerScopePhase, (*functionBody)))
            {
                (*functionBody)->DumpScopes();
            }
#endif

#if ENABLE_NATIVE_CODEGEN
            if ((!PHASE_OFF(Js::BackEndPhase, *functionBody))
                && !this->scriptContext->GetConfig()->IsNoNative()
                && !(*functionBody)->GetIsAsmjsMode())
            {
                GenerateFunction(this->scriptContext->GetNativeCodeGenerator(), *functionBody);
            }
#endif // ENABLE_NATIVE_CODEGEN

            (*functionBody)->m_isPartialDeserializedFunction = false;
        }
        else
        {
            *function = (*function)->Parse(nullptr, true);
        }

        return S_OK;
    }

    // Read the top function body.
    HRESULT ReadTopFunctionBody(FunctionBody** function, Utf8SourceInfo* sourceInfo, ByteCodeCache * cache, bool allowDefer, NativeModule *nativeModule)
    {
        auto topFunction = ReadInt32(functions, &functionCount);
        firstFunctionId = sourceInfo->GetSrcInfo()->sourceContextInfo->nextLocalFunctionId;
        sourceInfo->GetSrcInfo()->sourceContextInfo->nextLocalFunctionId += functionCount;
        sourceInfo->EnsureInitialized(functionCount);
        sourceInfo->GetSrcInfo()->sourceContextInfo->EnsureInitialized();

#if ENABLE_NATIVE_CODEGEN && defined(ENABLE_PREJIT)
        bool prejit = false;
        prejit = (!scriptContext->GetConfig()->IsNoNative() && Js::Configuration::Global.flags.Prejit && nativeModule == nullptr);
        allowDefer = allowDefer && !prejit;
#endif

        FunctionBody* functionBody = NULL;
        auto result = ReadFunctionBody(topFunction, (FunctionProxy **)&functionBody, sourceInfo, cache, nativeModule, true, !allowDefer /* don't deserialize nested if defer is allowed */);

        (*function) = functionBody;

#if ENABLE_NATIVE_CODEGEN && defined(ENABLE_PREJIT)
        if (prejit)
        {
            Assert(!allowDefer);
            GenerateAllFunctions(scriptContext->GetNativeCodeGenerator(), functionBody);
        }
#endif

        return result;
    }


    // Deserialize and save a PropertyIdArray
    const byte *
    DeserializePropertyIdArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto, FunctionBody * functionBody)
    {
        auto serialized = (serialization_alignment const Js::SerializedPropertyIdArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxPropIdArray);
#endif
        auto propertyCount = serialized->propertyCount;
        auto extraSlotCount = serialized->extraSlots;

        Assert(serialized->offset + sizeof(PropertyIdArray) < deserializeInto->GetLength());
        auto result = (PropertyIdArray *)(deserializeInto->GetBuffer() + serialized->offset);
        result->count = propertyCount;
        Assert(serialized->offset + result->GetDataSize(extraSlotCount) <= deserializeInto->GetLength());
        result->hadDuplicates = serialized->hadDuplicates;
        result->has__proto__ = serialized->has__proto__;

        auto elements = (PropertyId*)(serialized + 1);
        for(int i=0;i<propertyCount;++i)
        {
            result->elements[i] = functionBody->GetByteCodeCache()->LookupPropertyId(elements[i]);
        }
        for(int i=0;i<extraSlotCount;++i)
        {
            result->elements[propertyCount + i] = elements[propertyCount + i];
        }
        auto current = buffer +
            sizeof(serialization_alignment const Js::SerializedPropertyIdArray) + (propertyCount + extraSlotCount) * sizeof(PropertyId);
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxPropIdArray);
#endif
        return current;
    }

    // Deserialize and save a FuncInfoArray
    const byte *
    DeserializeFuncInfoArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto)
    {
        auto serialized = (serialization_alignment const SerializedFuncInfoArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxFuncInfoArray);
#endif
        auto count = serialized->count;

        Assert(serialized->offset + sizeof(AuxArray<FuncInfoEntry>) < deserializeInto->GetLength());
        auto result = (AuxArray<FuncInfoEntry> *)(deserializeInto->GetBuffer() + serialized->offset);
        result->count = count;
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());

        auto elements = (int*)(serialized+1);
        for(int i=0;i<count;++i)
        {
            result->elements[i].nestedIndex = elements[i*2];
            result->elements[i].scopeSlot = elements[i*2+1];
        }
        auto current = buffer + sizeof(serialization_alignment const SerializedFuncInfoArray) + (count * 2 * sizeof(int));
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxFuncInfoArray);
#endif
        return current;
    }

    // Deserialize a var array
    template<typename T>
    const byte * DeserializeVarArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto)
    {
        auto serialized = (serialization_alignment const Js::SerializedVarArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxVarArray);
#endif
        Assert(serialized->offset + sizeof(T) < deserializeInto->GetLength());
        auto result = (T *)(deserializeInto->GetBuffer() + serialized->offset);
        uint count = serialized->varCount;
        result->SetCount(count);
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());

        auto content = (const byte*)(serialized + 1);
        auto current = content;
        for (uint index = 0; index < count; index++)
        {
            byte code;
            current = ReadByte(current, &code);
            switch(code)
            {
                case ctInt:
                    {
                        int value;
                        current = ReadConstantSizedInt32(current, &value);
                        result->elements[index] = Js::TaggedInt::ToVarUnchecked(value);
                        break;
                    }
                case ctNumber:
                    {
                        double value;
                        current = ReadDouble(current, &value);
                        const auto number = Js::JavascriptNumber::New(value, scriptContext);
#if !FLOATVAR
                        scriptContext->BindReference(number);
#endif
                        result->elements[index] = number;
                        break;
                    }

                default:
                    AssertMsg(UNREACHED, "Unexpected object type in VarArray");
                    Throw::FatalInternalError();
            }
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxVarArray);
#endif
        return current;
    }

    const byte * DeserializeIntArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto)
    {
        auto serialized = (serialization_alignment const Js::SerializedIntArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxIntArray);
#endif
        Assert(serialized->offset + sizeof(AuxArray<int>) < deserializeInto->GetLength());
        auto result = (AuxArray<int> *)(deserializeInto->GetBuffer() + serialized->offset);
        uint count = serialized->intCount;
        result->count = count;
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());

        auto content = (const byte*)(serialized + 1);
        auto current = content;
        for (uint index = 0; index < count; index++)
        {
            int32 value;
            current = ReadConstantSizedInt32(current, &value);
            result->elements[index] = value;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxIntArray);
#endif
        return current;
    }

    const byte *
    DeserializeFloatArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto)
    {
        auto serialized = (serialization_alignment const SerializedFloatArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxFltArray);
#endif
        Assert(serialized->offset + sizeof(AuxArray<double>) < deserializeInto->GetLength());
        auto result = (AuxArray<double> *)(deserializeInto->GetBuffer() + serialized->offset);
        uint count = serialized->floatCount;
        result->count = count;
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());

        auto content = (const byte*)(serialized + 1);
        auto current = content;
        for (uint index = 0; index < count; index++)
        {
            double value;
            current = ReadDouble(current, &value);
            result->elements[index] = value;
        }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxFltArray);
#endif
        return current;
    }

};

// Construct the byte code cache. Copy things needed by inline 'Lookup' functions from reader.
ByteCodeCache::ByteCodeCache(ScriptContext * scriptContext, ByteCodeBufferReader * reader, int builtInPropertyCount)
    : reader(reader), propertyCount(reader->string16Count), builtInPropertyCount(builtInPropertyCount)
{
    auto alloc = scriptContext->SourceCodeAllocator();
    propertyIds = AnewArray(alloc, PropertyId, propertyCount);
    for (auto i=0; i < propertyCount; ++i)
    {
        propertyIds[i] = -1;
    }

    raw = reader->raw;

    // Read and populate PropertyIds
    for(int i=0; i < propertyCount; ++i)
    {
        PopulateLookupPropertyId(scriptContext, i);
    }
}

// Deserialize and save a PropertyId
void ByteCodeCache::PopulateLookupPropertyId(ScriptContext * scriptContext, int realOffset)
{
    PropertyId idInCache = realOffset + this->builtInPropertyCount;
    bool isPropertyRecord;
    auto propertyName = reader->GetString16ById(idInCache, &isPropertyRecord);
    if(isPropertyRecord)
    {
        auto propertyNameLength = reader->GetString16LengthById(idInCache);

        const Js::PropertyRecord * propertyRecord = scriptContext->GetThreadContext()->GetOrAddPropertyRecordBind(
            JsUtil::CharacterBuffer<wchar_t>(propertyName, propertyNameLength));

        propertyIds[realOffset] = propertyRecord->GetPropertyId();
    }
}

// Serialize function body
HRESULT ByteCodeSerializer::SerializeToBuffer(ScriptContext * scriptContext, ArenaAllocator * alloc, DWORD sourceByteLength, LPCUTF8 utf8Source, DWORD dwFunctionTableLength, BYTE * functionTable, FunctionBody * function, SRCINFO const* srcInfo, bool allocateBuffer, byte ** buffer, DWORD * bufferBytes, DWORD dwFlags)
{

    int builtInPropertyCount = (dwFlags & GENERATE_BYTE_CODE_BUFFER_LIBRARY) != 0 ?  PropertyIds::_countJSOnlyProperty : TotalNumberOfBuiltInProperties;

    Utf8SourceInfo *utf8SourceInfo = function->GetUtf8SourceInfo();

    HRESULT hr = utf8SourceInfo->EnsureLineOffsetCacheNoThrow();

    if (FAILED(hr))
    {
        return hr;
    }

    int32 sourceCharLength = utf8SourceInfo->GetCchLength();
    ByteCodeBufferBuilder builder(sourceByteLength, sourceCharLength, utf8Source, dwFunctionTableLength, functionTable, utf8SourceInfo, scriptContext, alloc, dwFlags, builtInPropertyCount);
    hr = builder.AddTopFunctionBody(function, srcInfo);

    if (SUCCEEDED(hr))
    {
        hr = builder.Create(allocateBuffer, buffer, bufferBytes);
    }

#if INSTRUMENT_BUFFER_INTS
    for (int i = 0; i < 4; i++)
    {
        printf("[BCGENSTATS] %d, %d\n", i, Counts[i]);
    }
#endif
    return hr;
}

HRESULT ByteCodeSerializer::DeserializeFromBuffer(ScriptContext * scriptContext, ulong scriptFlags, LPCUTF8 utf8Source, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, FunctionBody** function, uint sourceIndex)
{
    return ByteCodeSerializer::DeserializeFromBufferInternal(scriptContext, scriptFlags, utf8Source, /* sourceHolder */ nullptr, srcInfo, buffer, nativeModule, function, sourceIndex);
}
// Deserialize function body from supplied buffer
HRESULT ByteCodeSerializer::DeserializeFromBuffer(ScriptContext * scriptContext, ulong scriptFlags, ISourceHolder* sourceHolder, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, FunctionBody** function, uint sourceIndex)
{
    AssertMsg(sourceHolder != nullptr, "SourceHolder can't be null, if you have an empty source then pass ISourceHolder::GetEmptySourceHolder()");
    return ByteCodeSerializer::DeserializeFromBufferInternal(scriptContext, scriptFlags, /* utf8Source */ nullptr, sourceHolder, srcInfo, buffer, nativeModule, function, sourceIndex);
}
HRESULT ByteCodeSerializer::DeserializeFromBufferInternal(ScriptContext * scriptContext, ulong scriptFlags, LPCUTF8 utf8Source, ISourceHolder* sourceHolder, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, FunctionBody** function, uint sourceIndex)
{
    //ETW Event start
    JS_ETW(EventWriteJSCRIPT_BYTECODEDESERIALIZE_START(scriptContext, 0));

    auto alloc = scriptContext->SourceCodeAllocator();
    bool isLibraryCode = ((scriptFlags & fscrIsLibraryCode) == fscrIsLibraryCode);
    int builtInPropertyCount = isLibraryCode ? PropertyIds::_countJSOnlyProperty : TotalNumberOfBuiltInProperties;
    auto reader = Anew(alloc, ByteCodeBufferReader, scriptContext, buffer, isLibraryCode, builtInPropertyCount);
    auto hr = reader->ReadHeader();
    if (FAILED(hr))
    {
        return hr;
    }

    ENTER_PINNED_SCOPE(Js::Utf8SourceInfo, sourceInfo);
    ENTER_PINNED_SCOPE(SRCINFO const, pinnedSrcInfo);
    pinnedSrcInfo = srcInfo;

    if(sourceIndex == Js::Constants::InvalidSourceIndex)
    {
        if (sourceHolder == nullptr)
        {
            sourceHolder = utf8Source == nullptr ? ISourceHolder::GetEmptySourceHolder() : RecyclerNew(scriptContext->GetRecycler(), SimpleSourceHolder, utf8Source, reader->sourceSize);
        }

        sourceInfo = Js::Utf8SourceInfo::NewWithHolder(scriptContext, sourceHolder, reader->sourceCharLength, pinnedSrcInfo, isLibraryCode);

        reader->utf8SourceInfo = sourceInfo;
        reader->sourceIndex = scriptContext->SaveSourceNoCopy(sourceInfo, reader->sourceCharLength, false);

        sourceInfo->CreateLineOffsetCache(reader->lineInfoCache, reader->lineInfoCacheCount);
    }
    else
    {
        Assert(CONFIG_FLAG(ForceSerialized));
        sourceInfo = scriptContext->GetSource(sourceIndex);
        reader->utf8SourceInfo = sourceInfo;
        reader->sourceIndex = sourceIndex;
    }
    auto cache = Anew(alloc, ByteCodeCache, scriptContext, reader, builtInPropertyCount);

    hr = reader->ReadTopFunctionBody(function, sourceInfo, cache, ((scriptFlags & fscrAllowFunctionProxy) == fscrAllowFunctionProxy), nativeModule);

    //ETW Event stop
    JS_ETW(EventWriteJSCRIPT_BYTECODEDESERIALIZE_STOP(scriptContext,0));

    LEAVE_PINNED_SCOPE();
    LEAVE_PINNED_SCOPE();

    return hr;
}

void ByteCodeSerializer::ReadSourceInfo(const DeferDeserializeFunctionInfo* deferredFunction, int& lineNumber, int& columnNumber, bool& m_isEval, bool& m_isDynamicFunction)
{
    ByteCodeCache* cache = deferredFunction->m_cache;
    ByteCodeBufferReader* reader = cache->GetReader();
    reader->ReadSourceInfo(deferredFunction->m_functionBytes, lineNumber, columnNumber, m_isEval, m_isDynamicFunction);
}

FunctionBody* ByteCodeSerializer::DeserializeFunction(ScriptContext* scriptContext, DeferDeserializeFunctionInfo* deferredFunction)
{
    FunctionBody* deserializedFunctionBody = nullptr;
    ByteCodeCache* cache = deferredFunction->m_cache;
    ByteCodeBufferReader* reader = cache->GetReader();
    HRESULT hr = reader->ReadFunctionBody(deferredFunction->m_functionBytes, (FunctionProxy **)&deserializedFunctionBody, deferredFunction->GetUtf8SourceInfo(), cache, deferredFunction->m_nativeModule, true /* deserialize this */, false /* deserialize nested functions */, deferredFunction);
    if (FAILED(hr))
    {
        // This should never happen as the code is currently
        // structured since we validate the serialized bytecode during creation
        // of function proxies. In the future though, when we reorganize the byte
        // code file format, we could hit this error, in which case we
        // need a strategy to deal with this.
        Assert(false);
        Js::Throw::InternalError();
    }

    return deserializedFunctionBody;
}

SerializedAuxiliary::SerializedAuxiliary( uint offset, SerializedAuxiliaryKind kind ) :
    offset(offset), kind(kind)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , auxMagic(magicStartOfAux)
#endif
{
}


SerializedVarArray::SerializedVarArray( uint offset, bool isVarCount, int varCount ) :
    SerializedAuxiliary(offset, isVarCount ? sakVarArrayVarCount : sakVarArrayIntCount), varCount(varCount)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxVarArray)
#endif
{

}

SerializedIntArray::SerializedIntArray( uint offset, int intCount ) :
    SerializedAuxiliary(offset, sakIntArray), intCount(intCount)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxIntArray)
#endif
{

}

SerializedFloatArray::SerializedFloatArray( uint offset, int floatCount ) :
    SerializedAuxiliary(offset, sakFloatArray), floatCount(floatCount)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxFltArray)
#endif
{

}

SerializedPropertyIdArray::SerializedPropertyIdArray( uint offset, int propertyCount, int extraSlots, bool hadDuplicates, bool has__proto__) :
    SerializedAuxiliary(offset, sakPropertyIdArray), propertyCount(propertyCount), extraSlots(extraSlots), hadDuplicates(hadDuplicates), has__proto__(has__proto__)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxPropIdArray)
#endif
{

}


SerializedFuncInfoArray::SerializedFuncInfoArray( uint offset, int count ) :
    SerializedAuxiliary(offset, sakFuncInfoArray), count(count)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxFuncInfoArray)
#endif
{

}

}
