blob: 2181aa56517707d3e57d61d501d2f4678385c57b [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation and contributors. 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";
const int magicStartOfPropIdsOfFormals = *(int*)"propIdOfFormals[";
const int magicEndOfPropIdsOfFormals = *(int*)"]propIdOfFormals";
#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.
LibraryByteCodeVersioningScheme = 0,
#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_SERIALIZABLE_ACCESSOR_FIELD(type, name, serializableType) bool has_##name : 1
#define DEFINE_ALL_FIELDS
struct SerializedFieldList {
#include "SerializableFunctionFields.h"
bool has_m_lineNumber: 1;
bool has_m_columnNumber: 1;
bool has_m_nestedCount: 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 char16 * s16;
const char * s8;
};
public:
ByteBuffer(uint32 byteCount, void * pv)
: byteCount(byteCount), pv(pv)
{ }
};
} // namespace Js
template<>
struct DefaultComparer<Js::ByteBuffer*>
{
static bool Equals(Js::ByteBuffer const * str1, Js::ByteBuffer const * str2)
{
if (str1->byteCount != str2->byteCount)
{
return false;
}
return memcmp(str1->pv, str2->pv, str1->byteCount)==0;
}
static hash_t GetHashCode(Js::ByteBuffer const * str)
{
return JsUtil::CharacterBuffer<char>::StaticGetHashCode(str->s8, str->byteCount);
}
};
namespace Js
{
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,
ffIsAnonymous = 0x100000,
ffUsesArgumentsObject = 0x200000,
ffDoScopeObjectCreation = 0x400000,
ffIsParamAndBodyScopeMerged = 0x800000
};
// 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, _u("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, _u("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, _u("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;
//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 GenerateByteCodeForNative() const
{
return (dwFlags & GENERATE_BYTE_CODE_FOR_NATIVE) != 0;
}
public:
ByteCodeBufferBuilder(uint32 sourceSize, uint32 sourceCharLength, LPCUTF8 utf8Source, Utf8SourceInfo* sourceInfo, ScriptContext * scriptContext, ArenaAllocator * alloc, DWORD dwFlags, int builtInPropertyCount)
: magic(_u("Magic"), magicConstant),
totalSize(_u("Total Size"), 0),
fileVersionKind(_u("FileVersionKind"), 0),
V1(_u("V1"), 0),
V2(_u("V2"), 0),
V3(_u("V3"), 0),
V4(_u("V4"), 0),
architecture(_u("Expected Architecture"), magicArchitecture),
expectedFunctionBodySize(_u("Expected Function Body Size"), sizeof(unaligned FunctionBody)),
expectedBuildInPropertyCount(_u("Expected Built-in Properties"), builtInPropertyCount),
expectedOpCodeCount(_u("Expected Number of OpCodes"), (int)OpCode::Count),
originalSourceSize(_u("Source Size"), sourceSize),
originalCharLength(_u("Source Char Length"), sourceCharLength),
string16sOffset(_u("Offset of String16s"), &string16Count),
sourceSpansOffset(_u("Offset of Source Spans"), &sourceSpans),
lineInfoCacheOffset(_u("Offset of Source Spans"), &lineInfoCacheCount),
functionCount(_u("Function Count"), 0),
functionsOffset(_u("Offset of Functions"), &functionCount),
string16Count(_u("String16 Count"), 0),
string16IndexTable(_u("String16 Indexes")),
string16Table(_u("String16 Table")),
alignedString16Table(_u("Alignment for String16 Table"), &string16Table, sizeof(char16)),
sourceSpans(_u("Source Spans")),
lineInfoCacheCount(_u("Line Info Cache"), sourceInfo->GetLineOffsetCache()->GetLineCount()),
lineInfoCache(_u("Line Info Cache"), lineInfoCacheCount.value * sizeof(JsUtil::LineOffsetCache<Recycler>::LineOffsetCacheItem), (byte *)sourceInfo->GetLineOffsetCache()->GetItems()),
functionsTable(_u("Functions")),
nextString16Id(builtInPropertyCount), // Reserve the built-in property ids
topFunctionId(0),
utf8Source(utf8Source),
scriptContext(scriptContext),
startOfCachedScopeAuxBlock(nullptr),
alloc(alloc),
dwFlags(dwFlags),
builtInPropertyCount(builtInPropertyCount)
{
if (GenerateLibraryByteCode())
{
expectedFunctionBodySize.value = 0;
expectedOpCodeCount.value = 0;
}
// Library bytecode uses its own scheme
byte actualFileVersionScheme = GenerateLibraryByteCode() ? LibraryByteCodeVersioningScheme : CurrentFileVersionScheme;
#if ENABLE_DEBUG_CONFIG_OPTIONS
if (Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema)
{
actualFileVersionScheme = (byte)Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema;
}
#endif
fileVersionKind.value = actualFileVersionScheme;
switch (actualFileVersionScheme)
{
case EngineeringVersioningScheme:
{
Assert(!GenerateLibraryByteCode());
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;
break;
}
case ReleaseVersioningScheme:
{
Assert(!GenerateLibraryByteCode());
auto guidDWORDs = (DWORD*)(&byteCodeCacheReleaseFileVersion);
V1.value = guidDWORDs[0];
V2.value = guidDWORDs[1];
V3.value = guidDWORDs[2];
V4.value = guidDWORDs[3];
break;
}
case LibraryByteCodeVersioningScheme:
{
Assert(GenerateLibraryByteCode());
// To keep consistent library code between Chakra.dll and ChakraCore.dll, use a fixed version.
// This goes hand in hand with the bytecode verification unit tests.
V1.value = 0;
V2.value = 0;
V3.value = 0;
V4.value = 0;
break;
}
default:
Throw::InternalError();
break;
}
#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(_u("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 = regex::ImmutableList<Js::BufferBuilder*>::OfSingle(&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, _u("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, _u("First String16 Index"), stringEntry);
string16IndexTable.list = regex::ImmutableList<Js::BufferBuilder*>::OfSingle(stringIndexEntry, alloc);
PrependByte(string16IndexTable, _u("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, _u("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, _u("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(char16);
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;
};
#ifdef ASMJS_PLAT
HRESULT RewriteAsmJsByteCodesInto(BufferBuilderList & builder, LPCWSTR clue, FunctionBody * function, ByteBlock * byteBlock)
{
SListCounted<AuxRecord> auxRecords(alloc);
auto finalSize = Anew(alloc, BufferBuilderInt32, _u("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())
{
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 = reader.ReadAsmJsOp(layoutSize);
if (op == OpCodeAsmJs::EndOfBlock)
{
saveBlock();
break;
}
OpLayoutTypeAsmJs layoutType = OpCodeUtilAsmJs::GetOpCodeLayout(op);
switch (layoutType)
{
#define LAYOUT_TYPE(layout) \
case OpLayoutTypeAsmJs::##layout: { \
Assert(layoutSize == SmallLayout); \
reader.##layout(); \
saveBlock(); \
break; }
#define LAYOUT_TYPE_WMS(layout) \
case OpLayoutTypeAsmJs::##layout: { \
switch (layoutSize) \
{ \
case SmallLayout: \
reader.##layout##_Small(); \
break; \
case MediumLayout: \
reader.##layout##_Medium(); \
break; \
case LargeLayout: \
reader.##layout##_Large(); \
break; \
default: \
Assume(UNREACHED); \
} \
saveBlock(); \
break; }
#include "LayoutTypesAsmJs.h"
default:
AssertMsg(false, "Unknown OpLayout");
cantGenerate = true;
break;
}
}
if (cantGenerate)
{
return ByteCodeSerializer::CantGenerate;
}
if (size != byteBlock->GetLength() && !GenerateByteCodeForNative())
{
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);
auto finalSize = Anew(alloc, BufferBuilderInt32, _u("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())
{
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;
}
};
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(BrReg1Unsigned1);
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_WITH_ONEBYTE(ElementScopedC);
DEFAULT_LAYOUT_WITH_ONEBYTE(ElementScopedC2);
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;
}
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())
{
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, _u("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>, _u("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, _u("Magic end of aux var array"), magicEndOfAuxVarArray);
PrependInt32(builder, _u("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>, _u("Int Array"), header);
builder.list = builder.list->Prepend(block, alloc);
for (int i=0;i<count; i++)
{
auto value = elements[i];
PrependConstantInt32(builder, _u("Integer Constant Value"), value);
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
PrependInt32(builder, _u("Magic end of aux int array"), magicEndOfAuxIntArray);
PrependInt32(builder, _u("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>, _u("Float Array"), header);
builder.list = builder.list->Prepend(block, alloc);
for (int i=0;i<count; i++)
{
auto value = elements[i];
PrependDouble(builder, _u("Number Constant Value"), value);
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
PrependInt32(builder, _u("Magic end of aux float array"), magicEndOfAuxFltArray);
PrependInt32(builder, _u("Magic end of aux"), magicEndOfAux);
#endif
return block;
};
auto writeAuxPropertyIdArray = [&](uint offset, byte extraSlots) -> BufferBuilder* {
const PropertyIdArray * propIds = reader.ReadPropertyIdArray(offset, functionBody);
typedef serialization_alignment SerializedPropertyIdArray T;
T header(offset, propIds->count, extraSlots, propIds->hadDuplicates, propIds->has__proto__);
auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, _u("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, _u("Encoded Property Id"), encoded);
}
auto slots = propIds->elements + propIds->count;
for(byte i=0; i<extraSlots; i++)
{
PrependConstantInt32(builder, _u("Extra Slot"), slots[i]);
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
PrependInt32(builder, _u("Magic end of aux section"), magicEndOfAuxPropIdArray);
PrependInt32(builder, _u("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>, _u("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, _u("FuncInfo nestedIndex"), funcInfo.nestedIndex);
PrependConstantInt32(builder, _u("FuncInfo scopeSlot"), funcInfo.scopeSlot);
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
PrependInt32(builder, _u("Magic end of aux section"), magicEndOfAuxFuncInfoArray);
PrependInt32(builder, _u("Magic end of aux"), magicEndOfAux);
#endif
return block;
};
PrependInt32(builder, _u("Auxiliary Size"),
functionBody->GetAuxiliaryData()? functionBody->GetAuxiliaryData()->GetLength() : 0);
PrependInt32(builder, _u("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 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, _u("Start String Constant"), magicStartStringConstant);
#endif
size += PrependBool(builder, _u("Is Property String"), VirtualTableInfo<Js::PropertyString>::HasVirtualTable(str));
auto bb = Anew(alloc, ByteBuffer, (str->GetLength() + 1) * sizeof(char16), (void*)str->GetSz());
size += PrependByteBuffer(builder, _u("String Constant 16 Value"), bb);
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("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, _u("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, _u("Null Frame Display"), ctNullDisplay);
}
else if (var == (Js::Var)&Js::StrictNullFrameDisplay)
{
return PrependByte(builder, _u("Strict Null Frame Display"), ctStrictNullDisplay);
}
auto typeId = JavascriptOperators::GetTypeId(var);
switch (typeId)
{
case TypeIds_Undefined:
return PrependByte(builder, _u("Undefined Constant"), ctUndefined);
case TypeIds_Null:
return PrependByte(builder, _u("Null Constant"), ctNull);
case TypeIds_Boolean:
return PrependByte(builder, _u("Boolean Constant"), JavascriptBoolean::FromVar(var)->GetValue()? ctTrue : ctFalse);
case TypeIds_Number:
{
auto size = PrependByte(builder, _u("Number Constant"), ctNumber);
return size + PrependDouble(builder, _u("Number Constant Value"), JavascriptNumber::GetValue(var));
}
case TypeIds_Integer:
{
auto size = PrependByte(builder, _u("Integer Constant"), ctInt);
return size + PrependConstantInt32(builder, _u("Integer Constant Value"), TaggedInt::ToInt32(var));
}
case TypeIds_String:
{
auto size = PrependByte(builder, _u("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, _u("String Template Callsite Constant"), ctStringTemplateCallsite);
return size + PrependStringTemplateCallsiteConstant(builder, var);
}
default:
AssertMsg(UNREACHED, "Unexpected object type in AddConstantTable");
Throw::FatalInternalError();
}
}
#ifdef ASMJS_PLAT
uint32 AddAsmJsConstantTable(BufferBuilderList & builder, FunctionBody * function)
{
uint32 size = 0;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("Start Constant Table"), magicStartOfConstantTable);
#endif
auto constTable = function->GetConstTable();
byte* tableEnd = (byte*)(constTable + function->GetConstantCount());
for (int i = 0; i < WAsmJs::LIMIT; ++i)
{
WAsmJs::Types type = (WAsmJs::Types)i;
WAsmJs::TypedSlotInfo* typedInfo = function->GetAsmJsFunctionInfo()->GetTypedSlotInfo(type);
uint32 constCount = typedInfo->constCount;
if (constCount > FunctionBody::FirstRegSlot)
{
uint32 typeSize = WAsmJs::GetTypeByteSize(type);
byte* byteTable = ((byte*)constTable) + typedInfo->constSrcByteOffset;
byteTable += typeSize * FunctionBody::FirstRegSlot;
for (uint32 reg = FunctionBody::FirstRegSlot; reg < constCount; ++reg)
{
switch (type)
{
case WAsmJs::INT32: PrependConstantInt32(builder, _u("Integer Constant Value"), *(int*)byteTable); break;
case WAsmJs::FLOAT32: PrependFloat(builder, _u("Float Constant Value"), *(float*)byteTable); break;
case WAsmJs::FLOAT64: PrependDouble(builder, _u("Double Constant Value"), *(double*)byteTable); break;
case WAsmJs::SIMD: PrependSIMDValue(builder, _u("SIMD Constant Value"), *(AsmJsSIMDValue*)byteTable); break;
CompileAssert(WAsmJs::LastType == WAsmJs::SIMD);
default:
Assert(UNREACHED);
Js::Throw::FatalInternalError();
break;
}
byteTable += typeSize;
}
if (byteTable > tableEnd)
{
Assert(UNREACHED);
Js::Throw::FatalInternalError();
}
}
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("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, _u("Start Constant Table"), magicStartOfConstantTable);
#endif
for (auto reg = FunctionBody::FirstRegSlot + 1; reg < function->GetConstantCount(); 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, _u("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, _u("Start PropertyIdsForScopeSlotsArray"), magicStartOfPropertyIdsForScopeSlotArray);
#endif
for (uint i = 0; i < function->scopeSlotArraySize; i++)
{
PropertyId propertyId = encodePossiblyBuiltInPropertyId(function->GetPropertyIdsForScopeSlotArray()[i]);
size += PrependInt32(builder, _u("PropertyIdsForScopeSlots"), propertyId);
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("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, _u("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, _u("SlotIndexesForDebuggerScopeSlots"), scopeProperty.location);
PropertyId propertyId = encodePossiblyBuiltInPropertyId(scopeProperty.propId);
size += PrependInt32(builder, _u("PropertyIdsForDebuggerScopeSlots"), propertyId);
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("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, _u("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, _u("SlotArrayDebuggerScope"), i);
// Store the count of properties for the scope.
int propertiesCount = debuggerScope->HasProperties() ? debuggerScope->scopeProperties->Count() : 0u;
size += PrependInt32(builder, _u("Debugger Scope Slot Array Property Count"), propertiesCount);
size += AddSlotArrayDebuggerScopeProperties(builder, debuggerScope, propertiesCount);
slotArrayCount++;
}
}
Assert(debuggerScopeSlotArraySize == slotArrayCount);
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("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, _u("Start CacheId-to-PropertyId map"), magicStartOfCacheIdToPropIdMap);
#endif
for (uint i = 0; i < count; i++)
{
PropertyId propertyId = encodePossiblyBuiltInPropertyId(function->GetPropertyIdFromCacheId(i));
size += PrependInt32(builder, _u("CacheIdToPropertyId"), propertyId);
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("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, _u("Start Referenced-PropertyId map"), magicStartOfReferencedPropIdMap);
#endif
for (uint i = 0; i < count; i++)
{
PropertyId propertyId = encodeNonBuiltinPropertyId(function->GetReferencedPropertyIdWithMapIndex(i));
size += PrependInt32(builder, _u("ReferencedPropertyId"), propertyId);
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("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, _u("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, _u("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, _u("Statement Buffer"), spanSequence->pStatementBuffer);
size += PrependGrowingUint32Array(builder, _u("Actual Offset List"), spanSequence->pActualOffsetList);
return size;
}
template <typename TStructType>
uint32 PrependStruct(BufferBuilderList & builder, LPCWSTR clue, TStructType * value)
{
auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<TStructType>, clue, *value);
builder.list = builder.list->Prepend(entry, alloc);
return sizeof(serialization_alignment TStructType);
}
uint32 AddPropertyIdOfFormals(BufferBuilderList & builder, FunctionBody * function)
{
uint32 size = 0;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("Start propertyids of formals"), magicStartOfPropIdsOfFormals);
#endif
PropertyIdArray * propIds = function->GetFormalsPropIdArray(false);
if (propIds == nullptr)
{
size += PrependBool(builder, _u("ExportsIdArrayLength"), false);
}
else
{
size += PrependBool(builder, _u("ExportsIdArrayLength"), true);
byte extraSlotCount = 0;
if (function->HasCachedScopePropIds())
{
extraSlotCount = ActivationObjectEx::ExtraSlotCount();
}
size += PrependInt32(builder, _u("ExportsIdArrayLength"), propIds->count);
size += PrependByte(builder, _u("ExtraSlotsCount"), extraSlotCount);
size += PrependByte(builder, _u("ExportsIdArrayDups"), propIds->hadDuplicates);
size += PrependByte(builder, _u("ExportsIdArray__proto__"), propIds->has__proto__);
size += PrependByte(builder, _u("ExportsIdArrayHasNonSimpleParams"), propIds->hasNonSimpleParams);
for (uint i = 0; i < propIds->count; i++)
{
PropertyId propertyId = encodePossiblyBuiltInPropertyId(propIds->elements[i]);
size += PrependInt32(builder, _u("ExportsIdArrayElem"), propertyId);
}
auto slots = propIds->elements + propIds->count;
for (byte i = 0; i < extraSlotCount; i++)
{
size += PrependInt32(builder, _u("Extra Slot"), slots[i]);
}
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("End of prop ids for formals array"), magicEndOfPropIdsOfFormals);
#endif
return size;
}
#ifdef ASMJS_PLAT
uint32 AddAsmJsFunctionInfo(BufferBuilderList & builder, FunctionBody * function)
{
uint32 size = 0;
AsmJsFunctionInfo* funcInfo = function->GetAsmJsFunctionInfo();
#ifdef BYTE_CODE_MAGIC_CONSTANTS
PrependInt32(builder, _u("Start Asm.js Function Info"), magicStartOfAsmJsFuncInfo);
#endif
size += PrependInt32(builder, _u("ReturnType"), funcInfo->GetReturnType().which());
size += PrependInt16(builder, _u("ArgCount"), funcInfo->GetArgCount());
size += PrependInt16(builder, _u("ArgSize"), funcInfo->GetArgByteSize());
size += PrependInt16(builder, _u("ArgSizeArrayLength"), funcInfo->GetArgSizeArrayLength());
size += PrependUInt32Array(builder, funcInfo->GetArgSizeArrayLength(), funcInfo->GetArgsSizesArray());
size += PrependByteArray(builder, funcInfo->GetArgCount(), (byte*)funcInfo->GetArgTypeArray());
size += PrependByte(builder, _u("IsHeapBufferConst"), funcInfo->IsHeapBufferConst());
size += PrependByte(builder, _u("UsesHeapBuffer"), funcInfo->UsesHeapBuffer());
for (int i = WAsmJs::LIMIT - 1; i >= 0; --i)
{
const char16* clue = nullptr;
switch (i)
{
case WAsmJs::INT32: clue = _u("Int32TypedSlots"); break;
case WAsmJs::INT64: clue = _u("Int64TypedSlots"); break;
case WAsmJs::FLOAT32: clue = _u("Float32TypedSlots"); break;
case WAsmJs::FLOAT64: clue = _u("Float64TypedSlots"); break;
case WAsmJs::SIMD: clue = _u("SimdTypedSlots"); break;
default:
CompileAssert(WAsmJs::SIMD == WAsmJs::LastType);
Assert(false);
break;
}
size += PrependStruct<WAsmJs::TypedSlotInfo>(builder, clue, funcInfo->GetTypedSlotInfo((WAsmJs::Types)i));
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("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, _u("Start Asm.js Module Info"), magicStartOfAsmJsModuleInfo);
#endif
size += PrependInt32(builder, _u("ArgInCount"), moduleInfo->GetArgInCount());
size += PrependInt32(builder, _u("ExportsCount"), moduleInfo->GetExportsCount());
size += PrependInt32(builder, _u("SlotsCount"), moduleInfo->GetSlotsCount());
size += PrependInt32(builder, _u("SIMDRegCount"), moduleInfo->GetSimdRegCount());
if (moduleInfo->GetExportsCount() > 0)
{
PropertyIdArray * propArray = moduleInfo->GetExportsIdArray();
size += PrependByte(builder, _u("ExtraSlotsCount"), propArray->extraSlots);
size += PrependByte(builder, _u("ExportsIdArrayDups"), propArray->hadDuplicates);
size += PrependByte(builder, _u("ExportsIdArray__proto__"), propArray->has__proto__);
size += PrependInt32(builder, _u("ExportsIdArrayLength"), propArray->count);
for (uint i = 0; i < propArray->count; i++)
{
PropertyId propertyId = encodePossiblyBuiltInPropertyId(propArray->elements[i]);
size += PrependInt32(builder, _u("ExportsIdArrayElem"), propertyId);
}
size += PrependUInt32Array(builder, moduleInfo->GetExportsCount(), moduleInfo->GetExportsFunctionLocation());
}
size += PrependInt32(builder, _u("ExportFunctionIndex"), moduleInfo->GetExportFunctionIndex());
size += PrependInt32(builder, _u("VarCount"), moduleInfo->GetVarCount());
for (int i = 0; i < moduleInfo->GetVarCount(); i++)
{
size += PrependStruct(builder, _u("ModuleVar"), &moduleInfo->GetVar(i));
}
size += PrependInt32(builder, _u("VarImportCount"), moduleInfo->GetVarImportCount());
for (int i = 0; i < moduleInfo->GetVarImportCount(); i++)
{
auto import = moduleInfo->GetVarImport(i);
size += PrependInt32(builder, _u("ImportLocation"), import.location);
size += PrependByte(builder, _u("ImportType"), import.type);
PropertyId propertyId = encodePossiblyBuiltInPropertyId(import.field);
size += PrependInt32(builder, _u("ImportId"), propertyId);
}
size += PrependInt32(builder, _u("FunctionImportCount"), moduleInfo->GetFunctionImportCount());
for (int i = 0; i < moduleInfo->GetFunctionImportCount(); i++)
{
auto import = moduleInfo->GetFunctionImport(i);
size += PrependInt32(builder, _u("ImportLocation"), import.location);
PropertyId propertyId = encodePossiblyBuiltInPropertyId(import.field);
size += PrependInt32(builder, _u("ImportId"), propertyId);
}
size += PrependInt32(builder, _u("FunctionCount"), moduleInfo->GetFunctionCount());
for (int i = 0; i < moduleInfo->GetFunctionCount(); i++)
{
auto func = moduleInfo->GetFunction(i);
size += PrependInt32(builder, _u("FuncLocation"), func.location);
}
size += PrependInt32(builder, _u("FunctionTableCount"), moduleInfo->GetFunctionTableCount());
for (int i = 0; i < moduleInfo->GetFunctionTableCount(); i++)
{
auto table = moduleInfo->GetFunctionTable(i);
size += PrependInt32(builder, _u("FuncTableSize"), table.size);
PrependUInt32Array(builder, table.size, table.moduleFunctionIndex);
}
size += PrependStruct<AsmJsModuleMemory>(builder, _u("ModuleMemory"), &moduleInfo->GetModuleMemory());
size += PrependInt32(builder, _u("AsmJsSlotMapCount"), moduleInfo->GetAsmJsSlotMap()->Count());
auto slotIter = moduleInfo->GetAsmJsSlotMap()->GetIterator();
while (slotIter.IsValid())
{
PropertyId propertyId = encodePossiblyBuiltInPropertyId(slotIter.CurrentKey());
size += PrependInt32(builder, _u("AsmJsSlotPropId"), propertyId);
size += PrependStruct(builder, _u("AsmJsSlotValue"), slotIter.CurrentValue());
slotIter.MoveNext();
}
size += PrependStruct(builder, _u("MathBuiltinBV"), &moduleInfo->GetAsmMathBuiltinUsed());
size += PrependStruct(builder, _u("ArrayBuiltinBV"), &moduleInfo->GetAsmArrayBuiltinUsed());
size += PrependStruct(builder, _u("SIMDBuiltinBV"), &moduleInfo->GetAsmSimdBuiltinUsed());
size += PrependInt32(builder, _u("MaxHeapAccess"), moduleInfo->GetMaxHeapAccess());
size += PrependByte(builder, _u("UsesChangeHeap"), moduleInfo->GetUsesChangeHeap());
#ifdef BYTE_CODE_MAGIC_CONSTANTS
size += PrependInt32(builder, _u("End Asm.js Module Info"), magicEndOfAsmJsModuleInfo);
#endif
return size;
}
#endif
HRESULT AddFunctionBody(BufferBuilderList & builder, FunctionBody * function, SRCINFO const * srcInfo)
{
SerializedFieldList definedFields = { 0 };
#ifdef BYTE_CODE_MAGIC_CONSTANTS
PrependInt32(builder, _u("Start Function Table"), magicStartOfFunctionBody);
#endif
Assert(!function->GetIsSerialized());
DebugOnly(function->SetIsSerialized(true));
uint32 sourceDiff = 0;
if (!TryConvertToUInt32(function->StartOffset(), &sourceDiff))
{
Assert(0); // Likely a bug
return ByteCodeSerializer::CantGenerate;
}
bool isAnonymous = function->GetIsAnonymousFunction();
const char16* displayName = isAnonymous ? nullptr : function->GetDisplayName();
uint displayNameLength = isAnonymous ? 0 : function->m_displayNameLength;
PrependString16(builder, _u("Display Name"), displayName, (displayNameLength + 1)* sizeof(char16));
if (function->m_lineNumber != 0)
{
definedFields.has_m_lineNumber = true;
PrependInt32(builder, _u("Line Number"), function->m_lineNumber);
}
if (function->m_columnNumber != 0)
{
definedFields.has_m_columnNumber = true;
PrependInt32(builder, _u("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_doScopeObjectCreation ? ffDoScopeObjectCreation : 0)
| (function->m_usesArgumentsObject ? ffUsesArgumentsObject : 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)
| (function->m_isParamAndBodyScopeMerged ? ffIsParamAndBodyScopeMerged : 0)
| (isAnonymous ? ffIsAnonymous : 0)
#ifdef ASMJS_PLAT
| (function->m_isAsmjsMode ? ffIsAsmJsMode : 0)
| (function->m_isAsmJsFunction ? ffIsAsmJsFunction : 0)
#endif
;
PrependInt32(builder, _u("BitFlags"), bitFlags);
PrependInt32(builder, _u("Relative Function ID"), function->GetLocalFunctionId() - topFunctionId); // Serialized function ids are relative to the top function ID
PrependInt32(builder, _u("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
| FunctionInfo::Attributes::EnclosedByGlobalFunc
| FunctionInfo::Attributes::AllowDirectSuper)) == 0,
"Only the ErrorOnNew|SuperReference|Lambda|CapturesThis|Generator|ClassConstructor|Async|ClassMember|EnclosedByGlobalFunc|AllowDirectSuper attributes should be set on a serialized function");
PrependInt32(builder, _u("Offset Into Source"), sourceDiff);
if (function->GetNestedCount() > 0)
{
definedFields.has_m_nestedCount = true;
PrependInt32(builder, _u("Nested count"), function->GetNestedCount());
}
// This field should always be non-zero
Assert(function->GetConstantCount() != 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, _u(#name), function->##name); \
}
#include "SerializableFunctionFields.h"
{
#define DEFINE_FUNCTION_BODY_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
if (function->##name != 0) { \
definedFields.has_##name = true; \
Prepend##serializableType(builder, _u(#name), function->##name); \
}
#define DECLARE_SERIALIZABLE_ACCESSOR_FIELD(type, name, serializableType) \
if (function->Get##name##() != 0) { \
definedFields.has_##name = true; \
Prepend##serializableType(builder, _u(#name), function->Get##name##()); \
}
#include "SerializableFunctionFields.h"
}
{
auto loopHeaderArray = function->GetLoopHeaderArray();
if (loopHeaderArray)
{
PrependByte(builder, _u("Loop Header Array Exists"), 1);
uint loopCount = function->GetLoopCount();
for (uint i = 0; i < loopCount; ++i)
{
PrependInt32(builder, _u("Loop Header Start"), loopHeaderArray[i].startOffset);
PrependInt32(builder, _u("Loop Header End"), loopHeaderArray[i].endOffset);
}
}
else
{
PrependByte(builder, _u("Loop Header Array Exists"), 0);
}
#ifdef ASMJS_PLAT
if (function->GetAsmJsFunctionInfo())
{
PrependByte(builder, _u("Asm.js Info Exists"), 1);
AddAsmJsFunctionInfo(builder, function);
}
else if (function->GetIsAsmjsMode())
{
PrependByte(builder, _u("Asm.js Info Exists"), 2);
AddAsmJsModuleInfo(builder, function);
}
else
#endif
{
PrependByte(builder, _u("Asm.js Info Exists"), 0);
}
#ifdef ASMJS_PLAT
if (function->GetIsAsmJsFunction())
{
AddAsmJsConstantTable(builder, function);
auto hr = RewriteAsmJsByteCodesInto(builder, _u("Rewritten Byte Code"), function, function->byteCodeBlock);
if (FAILED(hr))
{
return hr;
}
}
else
#endif
{
AddConstantTable(builder, function);
auto hr = RewriteByteCodesInto(builder, _u("Rewritten Byte Code"), function, function->byteCodeBlock);
if (FAILED(hr))
{
return hr;
}
}
AddPropertyIdOfFormals(builder, function);
AddCacheIdToPropertyIdMap(builder, function);
AddReferencedPropertyIdMap(builder, function);
AddPropertyIdsForScopeSlotArray(builder, function);
uint debuggerScopeSlotArraySize = GetDebuggerScopeSlotArrayCount(function);
PrependInt32(builder, _u("Debugger Scope Slot Array Size"), debuggerScopeSlotArraySize);
AddSlotArrayDebuggerScopes(builder, function, debuggerScopeSlotArraySize);
// Literal regexes
for (uint i = 0; i < function->GetLiteralRegexCount(); ++i)
{
const auto literalRegex = function->GetLiteralRegex(i);
if (!literalRegex)
{
PrependInt32(builder, _u("Literal regex source length"), -1);
continue;
}
const auto source = literalRegex->GetSource();
PrependInt32(builder, _u("Literal regex source length"), source.GetLength());
PrependString16(builder, _u("Literal regex source"), source.GetBuffer(), (source.GetLength() + 1)* sizeof(char16));
PrependByte(builder, _u("Literal regex flags"), literalRegex->GetFlags());
}
// Write the SourceInfo stuff
PrependSmallSpanSequence(builder, _u("Span Sequence"), function->m_sourceInfo.pSpanSequence);
}
// Lastly, write each of the lexically enclosed functions
if (function->GetNestedCount())
{
auto nestedBodyList = Anew(alloc, BufferBuilderList, _u("Nest Function Bodies"));
for(uint32 i = 0; i<function->GetNestedCount(); ++i)
{
auto nestedFunctionBody = function->GetNestedFunc(i)->GetFunctionBody();
if (nestedFunctionBody==nullptr)
{
PrependInt32(builder, _u("Empty Nested Function"), 0);
}
else
{
auto nestedFunctionBuilder = Anew(alloc, BufferBuilderList, _u("Nested Function"));
nestedBodyList->list = nestedBodyList->list->Prepend(nestedFunctionBuilder, alloc);
auto offsetToNested = Anew(alloc, BufferBuilderRelativeOffset, _u("Offset To Nested Function"), nestedFunctionBuilder);
builder.list = builder.list->Prepend(offsetToNested, alloc);
AddFunctionBody(*nestedFunctionBuilder, nestedFunctionBody, srcInfo);
}
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
PrependInt32(builder, _u("End Function Body"), magicEndOfFunctionBody);
#endif
builder.list = builder.list->Prepend(nestedBodyList, alloc);
}
else
{
#ifdef BYTE_CODE_MAGIC_CONSTANTS
PrependInt32(builder, _u("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, _u("Serialized Field List"), &definedFields);
return S_OK;
}
HRESULT AddTopFunctionBody(FunctionBody * function, SRCINFO const * srcInfo)
{
topFunctionId = function->GetLocalFunctionId();
return AddFunctionBody(functionsTable, function, srcInfo);
}
};
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, _Out_ 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, uint32 * 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);
}
template <typename TStructType>
const byte * ReadStruct(const byte * buffer, serialization_alignment TStructType ** value)
{
*value = (serialization_alignment TStructType*)buffer;
return buffer + sizeof(serialization_alignment TStructType);
}
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;
}
template<typename Fn>
const byte * ReadByteBlock(const byte * buffer, Fn fn)
{
int contentLength;
buffer = ReadInt32(buffer, &contentLength);
fn(contentLength, 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? LibraryByteCodeVersioningScheme : 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;
switch (expectedFileVersionScheme)
{
case EngineeringVersioningScheme:
{
Js::VerifyCatastrophic(!isLibraryCode);
Js::VerifyOkCatastrophic(AutoSystemInfo::GetJscriptFileVersion(&expectedV1, &expectedV2, &expectedV3, &expectedV4));
break;
}
case ReleaseVersioningScheme:
{
Js::VerifyCatastrophic(!isLibraryCode);
auto guidDWORDs = (DWORD*)(&byteCodeCacheReleaseFileVersion);
expectedV1 = guidDWORDs[0];
expectedV2 = guidDWORDs[1];
expectedV3 = guidDWORDs[2];
expectedV4 = guidDWORDs[3];
break;
}
case LibraryByteCodeVersioningScheme:
{
// To keep consistent library code between Chakra.dll and ChakraCore.dll, use a fixed version.
// This goes hand in hand with the bytecode verification unit tests.
Js::VerifyCatastrophic(isLibraryCode);
expectedV1 = 0;
expectedV2 = 0;
expectedV3 = 0;
expectedV4 = 0;
break;
}
default:
Throw::InternalError();
break;
}
#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(char16));
string16Table = raw + string16TableOffset;
return S_OK;
}
const byte* ReadStringConstant(const byte* current, FunctionBody* function, _Out_ LPCWSTR * string, _Out_ uint32 * len, _Out_ bool * isPropertyString)
{
#ifdef BYTE_CODE_MAGIC_CONSTANTS
int constant;
current = ReadInt32(current, &constant);
Assert(constant == magicStartStringConstant);
#endif
current = ReadBool(current, isPropertyString);
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;
bool isPropertyString = false;
uint32 rawlen = 0;
for (int i = 0; i < arrayLength; i++)
{
current = ReadStringConstant(current, function, &string, &len, &isPropertyString);
JavascriptString* str = nullptr;
if (isPropertyString)
{
PropertyRecord const * propertyRecord;
scriptContext->GetOrAddPropertyRecord(string, len, &propertyRecord);
str = scriptContext->GetPropertyString(propertyRecord->GetPropertyId());
}
else
{
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, &isPropertyString);
rawlen += len;
JavascriptString* str = nullptr;
if (isPropertyString)
{
PropertyRecord const * propertyRecord;
scriptContext->GetOrAddPropertyRecord(string, len, &propertyRecord);
str = scriptContext->GetPropertyString(propertyRecord->GetPropertyId());
}
else
{
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;
}
#ifdef ASMJS_PLAT
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();
auto constTable = function->GetConstTable();
byte* tableEnd = (byte*)(constTable + function->GetConstantCount());
for (int i = 0; i < WAsmJs::LIMIT; ++i)
{
WAsmJs::Types type = (WAsmJs::Types)i;
WAsmJs::TypedSlotInfo* typedInfo = function->GetAsmJsFunctionInfo()->GetTypedSlotInfo(type);
uint32 constCount = typedInfo->constCount;
if (constCount > FunctionBody::FirstRegSlot)
{
uint32 typeSize = WAsmJs::GetTypeByteSize(type);
byte* byteTable = ((byte*)constTable) + typedInfo->constSrcByteOffset;
byteTable += typeSize * FunctionBody::FirstRegSlot;
size_t remainingBytes = (raw + totalSize) - current;
for (uint32 reg = FunctionBody::FirstRegSlot; reg < constCount; ++reg)
{
switch (type)
{
case WAsmJs::INT32: ReadConstantSizedInt32(current, remainingBytes, (int*)byteTable); break;
case WAsmJs::FLOAT32: ReadFloat(current, remainingBytes, (float*)byteTable); break;
case WAsmJs::FLOAT64: ReadDouble(current, remainingBytes, (double*)byteTable); break;
case WAsmJs::SIMD: ReadSIMDValue(current, remainingBytes, (AsmJsSIMDValue*)byteTable); break;
CompileAssert(WAsmJs::LastType == WAsmJs::SIMD);
default:
Assert(UNREACHED);
Js::Throw::FatalInternalError();
break;
}
current += typeSize;
byteTable += typeSize;
remainingBytes -= typeSize;
}
if (byteTable > tableEnd)
{
Assert(UNREACHED);
Js::Throw::FatalInternalError();
}
}
}
#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->GetConstantCount(); 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;
bool isPropertyString = false;
current = ReadStringConstant(current, function, &string, &len, &isPropertyString);
function->RecordStrConstant(reg, string, len, isPropertyString);
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, function->paramScopeSlotArraySize);
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;
}
const byte * ReadPropertyIdOfFormals(const byte * current, FunctionBody * function)
{
#ifdef BYTE_CODE_MAGIC_CONSTANTS
int constant = 0;
current = ReadInt32(current, &constant);
Assert(constant == magicStartOfPropIdsOfFormals);
#endif
bool isPropertyIdArrayAvailable = false;
current = ReadBool(current, &isPropertyIdArrayAvailable);
if (isPropertyIdArrayAvailable)
{
uint32 count = 0;
current = ReadUInt32(current, &count);
byte extraSlotCount = 0;
current = ReadByte(current, &extraSlotCount);
PropertyIdArray * propIds = function->AllocatePropertyIdArrayForFormals((extraSlotCount + count) * sizeof(PropertyId), count, extraSlotCount);
propIds->count = count;
bool hadDuplicates = false;
current = ReadBool(current, &hadDuplicates);
propIds->hadDuplicates = hadDuplicates;
bool has__proto__ = false;
current = ReadBool(current, &has__proto__);
propIds->has__proto__ = has__proto__;
bool hasNonSimpleParams = false;
current = ReadBool(current, &hasNonSimpleParams);
propIds->hasNonSimpleParams = hasNonSimpleParams;
int id = 0;
for (uint i = 0; i < propIds->count; ++i)
{
current = ReadInt32(current, &id);
PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(id);
propIds->elements[i] = propertyId;
}
for (int i = 0; i < extraSlotCount; ++i)
{
current = ReadInt32(current, &id);
propIds->elements[propIds->count + i] = id;
}
}
#ifdef BYTE_CODE_MAGIC_CONSTANTS
current = ReadInt32(current, &constant);
Assert(constant == magicEndOfPropIdsOfFormals);
#endif
return current;
}
#ifdef ASMJS_PLAT
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));
ArgSlot argCount;
current = ReadUInt16(current, &argCount);
funcInfo->SetArgCount(argCount);
ArgSlot argByteSize;
current = ReadUInt16(current, &argByteSize);
funcInfo->SetArgByteSize(argByteSize);
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]);
}
}
bool boolVal;
current = ReadBool(current, &boolVal);
funcInfo->SetIsHeapBufferConst(boolVal);
current = ReadBool(current, &boolVal);
funcInfo->SetUsesHeapBuffer(boolVal);
for (int i = WAsmJs::LIMIT - 1; i >= 0; --i)
{
serialization_alignment WAsmJs::TypedSlotInfo* info;
current = ReadStruct<WAsmJs::TypedSlotInfo>(current, &info);
WAsmJs::TypedSlotInfo* typedInfo = funcInfo->GetTypedSlotInfo((WAsmJs::Types)i);
*typedInfo = *info;
}
#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();
byte extraSlots;
current = ReadByte(current, &extraSlots);
propArray->extraSlots = extraSlots;
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;
current = ReadStruct<AsmJsModuleInfo::ModuleVar>(current, &modVar);
moduleInfo->SetVar(i, *modVar);
}
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;
FunctionProxy::SetDisplayNameFlags displayNameFlags = FunctionProxy::SetDisplayNameFlags::SetDisplayNameFlagsDontCopy;
const char16* displayName = nullptr;
if (bitflags & ffIsAnonymous)
{
displayName = Constants::AnonymousFunction;
}
else
{
if (deferDeserializeFunctionInfo != nullptr)
{
displayName = deferDeserializeFunctionInfo->GetDisplayName();
if (deferDeserializeFunctionInfo->GetDisplayNameIsRecyclerAllocated())
{
displayNameFlags = (FunctionProxy::SetDisplayNameFlags)(displayNameFlags | FunctionProxy::SetDisplayNameFlags::SetDisplayNameFlagsRecyclerAllocated);
}
}
else
{
displayName = GetString16ById(displayNameId);
}
}
uint displayNameLength = (bitflags & ffIsAnonymous) ? Constants::AnonymousFunctionLength :
deferDeserializeFunctionInfo ? deferDeserializeFunctionInfo->GetDisplayNameLength() :
GetString16LengthById(displayNameId);
uint displayShortNameOffset = deferDeserializeFunctionInfo ? deferDeserializeFunctionInfo->GetShortDisplayNameOffset() : 0;
int functionId;
current = ReadInt32(current, &functionId);
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_ConstantCount)
{
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,
Js::FunctionBody::FunctionBodyFlags::Flags_None // bytecode serializer will initialize
#ifdef PERF_COUNTERS
, (deferDeserializeFunctionInfo != nullptr)
#endif
);
(*functionBody)->SetDisplayName(displayName, displayNameLength, displayShortNameOffset, displayNameFlags);
Assert(!(*functionBody)->GetIsSerialized());
(*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,
Js::FunctionBody::FunctionBodyFlags::Flags_None);
}
// 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_doScopeObjectCreation = (bitflags & ffDoScopeObjectCreation) ? true : false;
(*function)->m_usesArgumentsObject = (bitflags & ffUsesArgumentsObject) ? 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); \
}
#include "SerializableFunctionFields.h"
if (definedFields->has_ConstantCount)
{
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_SERIALIZABLE_ACCESSOR_FIELD(type, name, serializableType) \
if (definedFields->has_##name == true) { \
type tmp##name=0; \
current = Read##serializableType(current, &tmp##name); \
(*functionBody)->Set##name##(tmp##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_FirstTmpRegister == false)
{
(*functionBody)->SetFirstTmpRegister(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;
(*functionBody)->m_isParamAndBodyScopeMerged = (bitflags & ffIsParamAndBodyScopeMerged) ? true : false;
#ifdef ASMJS_PLAT
(*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();
uint loopCount = (*functionBody)->GetLoopCount();
for (uint i = 0; i < 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);
#ifdef ASMJS_PLAT
if (asmJsInfoExists == 1)
{
current = ReadAsmJsFunctionInfo(current, *functionBody);
}
else if (asmJsInfoExists == 2)
{
current = ReadAsmJsModuleInfo(current, *functionBody);
}
else
#endif
{
Assert(asmJsInfoExists == 0);
}
// Read constants table
#ifdef ASMJS_PLAT
if ((*functionBody)->GetIsAsmJsFunction())
{
current = ReadAsmJsConstantsTable(current, *functionBody);
}
else
#endif
{
current = ReadConstantsTable(current, *functionBody);
}
// Byte code
current = ReadByteBlock(current, [&functionBody, this](int contentLength, const byte* buffer)
{
if (contentLength == 0)
{
(*functionBody)->byteCodeBlock = nullptr;
}
else
{
// TODO: Abstract this out to ByteBlock::New
(*functionBody)->byteCodeBlock = RecyclerNewLeaf(scriptContext->GetRecycler(), ByteBlock, contentLength, (byte*)buffer);
}
});
// Auxiliary
current = ReadAuxiliary(current, *functionBody);
current = ReadPropertyIdOfFormals(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();
(*functionBody)->AllocateForInCache();
// Literal regexes
(*functionBody)->AllocateLiteralRegexArray();
for (uint i = 0; i < (*functionBody)->GetLiteralRegexCount(); ++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->GetFunctionInfo(), 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_ConstantCount)
{
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(Field(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;
sourceInfo->ClearTopLevelFunctionInfoList();
sourceInfo->AddTopLevelFunctionInfo(functionBody->GetFunctionInfo(), this->scriptContext->GetRecycler());
#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;
result->extraSlots = extraSlotCount;
Assert(serialized->offset + result->GetDataSize() <= 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<char16>(propertyName, propertyNameLength));
propertyIds[realOffset] = propertyRecord->GetPropertyId();
}
}
// Serialize function body
HRESULT ByteCodeSerializer::SerializeToBuffer(ScriptContext * scriptContext, ArenaAllocator * alloc, DWORD sourceByteLength, LPCUTF8 utf8Source, 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, 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, uint32 scriptFlags, LPCUTF8 utf8Source, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, Field(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, uint32 scriptFlags, ISourceHolder* sourceHolder, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, Field(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, uint32 scriptFlags, LPCUTF8 utf8Source, ISourceHolder* sourceHolder, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, Field(FunctionBody*)* function, uint sourceIndex)
{
//ETW Event start
JS_ETW(EventWriteJSCRIPT_BYTECODEDESERIALIZE_START(scriptContext, 0));
auto alloc = scriptContext->SourceCodeAllocator();
bool isLibraryCode = ((scriptFlags & fscrIsLibraryCode) == fscrIsLibraryCode);
bool isJsBuiltInCode = ((scriptFlags & fscrJsBuiltIn) == fscrJsBuiltIn);
bool isLibraryOrJsBuiltInCode = isLibraryCode || isJsBuiltInCode;
int builtInPropertyCount = isLibraryOrJsBuiltInCode ? PropertyIds::_countJSOnlyProperty : TotalNumberOfBuiltInProperties;
auto reader = Anew(alloc, ByteCodeBufferReader, scriptContext, buffer, isLibraryOrJsBuiltInCode, 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, isLibraryOrJsBuiltInCode);
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, byte 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
{
}
} // namespace Js