| //------------------------------------------------------------------------------------------------------- |
| // 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, ®Slot); |
| 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 |