Bug 1890793: Assert CallArgs::newTarget is not gray. r=spidermonkey-reviewers,sfink...
[gecko.git] / dom / script / ScriptCompression.cpp
blob837fefe096ecbfe7dc413c660a5900715307d050
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "zlib.h"
8 #include "ScriptLoadRequest.h"
9 #include "ScriptLoader.h"
10 #include "mozilla/PerfStats.h"
11 #include "mozilla/ProfilerMarkers.h"
12 #include "mozilla/Vector.h"
13 #include "mozilla/ScopeExit.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/StaticPrefs_browser.h"
17 using namespace mozilla;
19 namespace JS::loader {
21 #undef LOG
22 #define LOG(args) \
23 MOZ_LOG(mozilla::dom::ScriptLoader::gScriptLoaderLog, \
24 mozilla::LogLevel::Debug, args)
27 * ScriptBytecodeDataLayout
29 * ScriptBytecodeDataLayout provides accessors to maintain the correct data
30 * layout of the ScriptLoadRequest's script bytecode buffer.
32 class ScriptBytecodeDataLayout {
33 public:
34 explicit ScriptBytecodeDataLayout(mozilla::Vector<uint8_t>& aBytecode,
35 size_t aBytecodeOffset)
36 : mBytecode(aBytecode), mBytecodeOffset(aBytecodeOffset) {}
38 uint8_t* prelude() const { return mBytecode.begin(); }
39 size_t preludeLength() const { return mBytecodeOffset; }
41 uint8_t* bytecode() const { return prelude() + mBytecodeOffset; }
42 size_t bytecodeLength() const { return mBytecode.length() - preludeLength(); }
44 mozilla::Vector<uint8_t>& mBytecode;
45 size_t mBytecodeOffset;
49 * ScriptBytecodeCompressedDataLayout
51 * ScriptBytecodeCompressedDataLayout provides accessors to maintain the correct
52 * data layout of a compressed script bytecode buffer.
54 class ScriptBytecodeCompressedDataLayout {
55 public:
56 using UncompressedLengthType = uint32_t;
58 explicit ScriptBytecodeCompressedDataLayout(
59 mozilla::Vector<uint8_t>& aBytecode, size_t aBytecodeOffset)
60 : mBytecode(aBytecode), mBytecodeOffset(aBytecodeOffset) {}
62 uint8_t* prelude() const { return mBytecode.begin(); }
63 size_t preludeLength() const { return mBytecodeOffset; }
65 uint8_t* uncompressedLength() const { return prelude() + mBytecodeOffset; }
66 size_t uncompressedLengthLength() const {
67 return sizeof(UncompressedLengthType);
70 uint8_t* bytecode() const {
71 return uncompressedLength() + uncompressedLengthLength();
73 size_t bytecodeLength() const {
74 return mBytecode.length() - uncompressedLengthLength() - preludeLength();
77 mozilla::Vector<uint8_t>& mBytecode;
78 size_t mBytecodeOffset;
81 bool ScriptBytecodeCompress(Vector<uint8_t>& aBytecodeBuf,
82 size_t aBytecodeOffset,
83 Vector<uint8_t>& aCompressedBytecodeBufOut) {
84 // TODO probably need to move this to a helper thread
86 AUTO_PROFILER_MARKER_TEXT("ScriptBytecodeCompress", JS, {}, ""_ns);
87 PerfStats::AutoMetricRecording<PerfStats::Metric::JSBC_Compression>
88 autoRecording;
90 ScriptBytecodeDataLayout uncompressedLayout(aBytecodeBuf, aBytecodeOffset);
91 ScriptBytecodeCompressedDataLayout compressedLayout(
92 aCompressedBytecodeBufOut, uncompressedLayout.preludeLength());
93 ScriptBytecodeCompressedDataLayout::UncompressedLengthType
94 uncompressedLength = uncompressedLayout.bytecodeLength();
95 z_stream zstream{.next_in = uncompressedLayout.bytecode(),
96 .avail_in = uncompressedLength};
98 // Note: deflateInit needs to be called before deflateBound.
99 const uint32_t compressionLevel =
100 StaticPrefs::browser_cache_jsbc_compression_level();
101 if (deflateInit(&zstream, compressionLevel) != Z_OK) {
102 LOG(
103 ("ScriptLoadRequest: Unable to initialize bytecode cache "
104 "compression."));
105 return false;
107 auto autoDestroy = MakeScopeExit([&]() { deflateEnd(&zstream); });
109 auto compressedLength = deflateBound(&zstream, uncompressedLength);
110 if (!aCompressedBytecodeBufOut.resizeUninitialized(
111 compressedLength + compressedLayout.preludeLength() +
112 compressedLayout.uncompressedLengthLength())) {
113 return false;
115 memcpy(compressedLayout.prelude(), uncompressedLayout.prelude(),
116 uncompressedLayout.preludeLength());
117 memcpy(compressedLayout.uncompressedLength(), &uncompressedLength,
118 sizeof(uncompressedLength));
119 zstream.next_out = compressedLayout.bytecode();
120 zstream.avail_out = compressedLength;
122 int ret = deflate(&zstream, Z_FINISH);
123 if (ret == Z_MEM_ERROR) {
124 return false;
126 MOZ_RELEASE_ASSERT(ret == Z_STREAM_END);
128 aCompressedBytecodeBufOut.shrinkTo(zstream.next_out -
129 aCompressedBytecodeBufOut.begin());
130 return true;
133 bool ScriptBytecodeDecompress(Vector<uint8_t>& aCompressedBytecodeBuf,
134 size_t aBytecodeOffset,
135 Vector<uint8_t>& aBytecodeBufOut) {
136 AUTO_PROFILER_MARKER_TEXT("ScriptBytecodeDecompress", JS, {}, ""_ns);
137 PerfStats::AutoMetricRecording<PerfStats::Metric::JSBC_Decompression>
138 autoRecording;
140 ScriptBytecodeDataLayout uncompressedLayout(aBytecodeBufOut, aBytecodeOffset);
141 ScriptBytecodeCompressedDataLayout compressedLayout(
142 aCompressedBytecodeBuf, uncompressedLayout.preludeLength());
143 ScriptBytecodeCompressedDataLayout::UncompressedLengthType uncompressedLength;
144 memcpy(&uncompressedLength, compressedLayout.uncompressedLength(),
145 compressedLayout.uncompressedLengthLength());
146 if (!aBytecodeBufOut.resizeUninitialized(uncompressedLayout.preludeLength() +
147 uncompressedLength)) {
148 return false;
150 memcpy(uncompressedLayout.prelude(), compressedLayout.prelude(),
151 compressedLayout.preludeLength());
153 z_stream zstream{nullptr};
154 zstream.next_in = compressedLayout.bytecode();
155 zstream.avail_in = static_cast<uint32_t>(compressedLayout.bytecodeLength());
156 zstream.next_out = uncompressedLayout.bytecode();
157 zstream.avail_out = uncompressedLength;
158 if (inflateInit(&zstream) != Z_OK) {
159 LOG(("ScriptLoadRequest: inflateInit FAILED (%s)", zstream.msg));
160 return false;
162 auto autoDestroy = MakeScopeExit([&]() { inflateEnd(&zstream); });
164 int ret = inflate(&zstream, Z_NO_FLUSH);
165 bool ok = (ret == Z_OK || ret == Z_STREAM_END) && zstream.avail_in == 0;
166 if (!ok) {
167 LOG(("ScriptLoadReques: inflate FAILED (%s)", zstream.msg));
168 return false;
171 return true;
174 #undef LOG
176 } // namespace JS::loader