|
|
@@ -40,6 +40,7 @@
|
|
|
#include "ByteCode/ByteCodeSerializer.h"
|
|
|
#include "Language/SimpleDataCacheWrapper.h"
|
|
|
#include "Core/CRC.h"
|
|
|
+#include "Common/CompressionUtilities.h"
|
|
|
|
|
|
namespace Js
|
|
|
{
|
|
|
@@ -2178,28 +2179,72 @@ namespace Js
|
|
|
}
|
|
|
|
|
|
// The block includes a 4-byte CRC before the parser state cache.
|
|
|
- ULONG byteCount = blockByteCount - sizeof(uint);
|
|
|
+ ULONG compressedBufferByteCount = blockByteCount - sizeof(uint);
|
|
|
|
|
|
// The contract for this bytecode buffer is that it is available as long as we have this ScriptContext.
|
|
|
// We will use this buffer as the string table needed to back the deferred stubs as well as bytecode
|
|
|
// for defer deserialized functions.
|
|
|
// TODO: This, better.
|
|
|
ArenaAllocator* alloc = this->SourceCodeAllocator();
|
|
|
- byte* buffer = AnewArray(alloc, byte, byteCount);
|
|
|
+ size_t decompressedBufferByteCount = 0;
|
|
|
+ byte* decompressedBuffer = nullptr;
|
|
|
|
|
|
- if (buffer == nullptr)
|
|
|
+ if (CONFIG_FLAG(CompressParserStateCache))
|
|
|
{
|
|
|
- return E_FAIL;
|
|
|
- }
|
|
|
+ BEGIN_TEMP_ALLOCATOR(tempAllocator, this, _u("ByteCodeSerializer"));
|
|
|
+ {
|
|
|
+ byte* compressedBuffer = AnewNoThrowArray(tempAllocator, byte, compressedBufferByteCount);
|
|
|
+ if (compressedBuffer == nullptr)
|
|
|
+ {
|
|
|
+ hr = E_FAIL;
|
|
|
+ goto ExitTempAllocator;
|
|
|
+ }
|
|
|
|
|
|
- hr = pDataCache->ReadArray(buffer, byteCount);
|
|
|
- if (FAILED(hr))
|
|
|
+ hr = pDataCache->ReadArray(compressedBuffer, compressedBufferByteCount);
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to read compressed parser state cache (wanted %lu bytes) (hr = 0x%08lx) for '%s'\n"), compressedBufferByteCount, hr, url);
|
|
|
+ goto ExitTempAllocator;
|
|
|
+ }
|
|
|
+
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Successfully read compressed parser state cache (%lu bytes) for '%s'\n"), compressedBufferByteCount, url);
|
|
|
+
|
|
|
+ hr = Js::CompressionUtilities::DecompressBuffer(alloc, compressedBuffer, compressedBufferByteCount, &decompressedBuffer, &decompressedBufferByteCount);
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to decompress parser state cache (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
+ goto ExitTempAllocator;
|
|
|
+ }
|
|
|
+
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Decompressed parser state cache %lu -> %lu bytes (%.2f%%) to stream for '%s'\n"), compressedBufferByteCount, decompressedBufferByteCount, (double)compressedBufferByteCount / decompressedBufferByteCount * 100.0, url);
|
|
|
+ }
|
|
|
+ExitTempAllocator:
|
|
|
+ END_TEMP_ALLOCATOR(tempAllocator, this);
|
|
|
+
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ goto Error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to read parser state cache (wanted %lu bytes, got %lu bytes) (hr = 0x%08lx) for '%s'\n"), byteCount, pDataCache->BytesWrittenInBlock(), hr, url);
|
|
|
- return hr;
|
|
|
+ // We didn't compress the parser state cache so don't decompress it, just read the buffer.
|
|
|
+ decompressedBuffer = AnewNoThrowArray(alloc, byte, compressedBufferByteCount);
|
|
|
+ decompressedBufferByteCount = compressedBufferByteCount;
|
|
|
+ if (decompressedBuffer == nullptr)
|
|
|
+ {
|
|
|
+ return E_FAIL;
|
|
|
+ }
|
|
|
+
|
|
|
+ hr = pDataCache->ReadArray(decompressedBuffer, compressedBufferByteCount);
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to read parser state cache (wanted %lu bytes) (hr = 0x%08lx) for '%s'\n"), decompressedBufferByteCount, hr, url);
|
|
|
+ goto Error;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Successfully read parser state cache (%lu bytes) for '%s'\n"), byteCount, url);
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Successfully read parser state cache (%lu bytes) for '%s'\n"), decompressedBufferByteCount, url);
|
|
|
|
|
|
if (utf8SourceInfo != nullptr)
|
|
|
{
|
|
|
@@ -2217,21 +2262,24 @@ namespace Js
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Trying to deserialize parser state cache for '%s'\n"), url);
|
|
|
|
|
|
FunctionBody* functionBody = nullptr;
|
|
|
- hr = Js::ByteCodeSerializer::DeserializeFromBuffer(this, grfscr, (ISourceHolder*) nullptr, srcInfo, buffer, nativeModule, &functionBody, sourceIndex);
|
|
|
+ hr = Js::ByteCodeSerializer::DeserializeFromBuffer(this, grfscr, (ISourceHolder*) nullptr, srcInfo, decompressedBuffer, nativeModule, &functionBody, sourceIndex);
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
{
|
|
|
- AdeleteArray(alloc, byteCount, buffer);
|
|
|
-
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to deserialize parser state cache (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
- return hr;
|
|
|
+ goto Error;
|
|
|
}
|
|
|
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Successfully deserialized parser state cache for '%s'\n"), url);
|
|
|
|
|
|
*func = functionBody->GetParseableFunctionInfo();
|
|
|
- *parserStateCacheBuffer = buffer;
|
|
|
- *parserStateCacheByteCount = byteCount;
|
|
|
+ *parserStateCacheBuffer = decompressedBuffer;
|
|
|
+ *parserStateCacheByteCount = (DWORD)decompressedBufferByteCount;
|
|
|
+Error:
|
|
|
+ if (FAILED(hr) && decompressedBuffer != nullptr)
|
|
|
+ {
|
|
|
+ AdeleteArray(alloc, decompressedBufferByteCount, decompressedBuffer);
|
|
|
+ }
|
|
|
#endif
|
|
|
|
|
|
return hr;
|
|
|
@@ -2289,29 +2337,61 @@ namespace Js
|
|
|
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Trying to write parser state cache (%lu bytes) to stream for '%s'\n"), serializeParserStateCacheSize, url);
|
|
|
|
|
|
- hr = pDataCache->StartBlock(Js::SimpleDataCacheWrapper::BlockType_ParserState, serializeParserStateCacheSize + sizeof(uint));
|
|
|
-
|
|
|
- if (FAILED(hr))
|
|
|
+ BEGIN_TEMP_ALLOCATOR(tempAllocator, this, _u("ByteCodeSerializer"));
|
|
|
{
|
|
|
- OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to write a block to the parser state cache data stream (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
- return hr;
|
|
|
- }
|
|
|
+ byte* compressedBuffer = nullptr;
|
|
|
+ size_t compressedSize = 0;
|
|
|
|
|
|
- hr = pDataCache->Write(sourceCRC);
|
|
|
+ if (CONFIG_FLAG(CompressParserStateCache))
|
|
|
+ {
|
|
|
+ hr = Js::CompressionUtilities::CompressBuffer(tempAllocator, serializeParserStateCacheBuffer, serializeParserStateCacheSize, &compressedBuffer, &compressedSize);
|
|
|
|
|
|
- OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Computed CRC value = 0x%08lx for '%s'\n"), sourceCRC, url);
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to compress parser state cache (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
+ goto ExitTempAllocator;
|
|
|
+ }
|
|
|
|
|
|
- if (FAILED(hr))
|
|
|
- {
|
|
|
- OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to write CRC data to the data stream (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
- return hr;
|
|
|
- }
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Compressed parser state cache %lu -> %lu bytes (%.2f%%) to stream for '%s'\n"), serializeParserStateCacheSize, compressedSize, (double)compressedSize / serializeParserStateCacheSize * 100.0, url);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Don't compress, just pass through the parser state cache buffer
|
|
|
+ compressedBuffer = serializeParserStateCacheBuffer;
|
|
|
+ compressedSize = serializeParserStateCacheSize;
|
|
|
+ }
|
|
|
|
|
|
- hr = pDataCache->WriteArray(serializeParserStateCacheBuffer, serializeParserStateCacheSize);
|
|
|
+ hr = pDataCache->StartBlock(Js::SimpleDataCacheWrapper::BlockType_ParserState, (ULONG)compressedSize + sizeof(uint));
|
|
|
+
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to write a block to the parser state cache data stream (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
+ goto ExitTempAllocator;
|
|
|
+ }
|
|
|
+
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Computed CRC value = 0x%08lx for '%s'\n"), sourceCRC, url);
|
|
|
+
|
|
|
+ hr = pDataCache->Write(sourceCRC);
|
|
|
+
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to write CRC data to the data stream (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
+ goto ExitTempAllocator;
|
|
|
+ }
|
|
|
+
|
|
|
+ hr = pDataCache->WriteArray(compressedBuffer, (ULONG)compressedSize);
|
|
|
+
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to write compressed parser state cache (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
+ goto ExitTempAllocator;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ExitTempAllocator:
|
|
|
+ END_TEMP_ALLOCATOR(tempAllocator, this);
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
{
|
|
|
- OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to write parser state cache (hr = 0x%08lx) for '%s'\n"), hr, url);
|
|
|
return hr;
|
|
|
}
|
|
|
|