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/. */
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
{
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
{
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
{
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
>
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
) {
103 ("ScriptLoadRequest: Unable to initialize bytecode cache "
107 auto autoDestroy
= MakeScopeExit([&]() { deflateEnd(&zstream
); });
109 auto compressedLength
= deflateBound(&zstream
, uncompressedLength
);
110 if (!aCompressedBytecodeBufOut
.resizeUninitialized(
111 compressedLength
+ compressedLayout
.preludeLength() +
112 compressedLayout
.uncompressedLengthLength())) {
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
) {
126 MOZ_RELEASE_ASSERT(ret
== Z_STREAM_END
);
128 aCompressedBytecodeBufOut
.shrinkTo(zstream
.next_out
-
129 aCompressedBytecodeBufOut
.begin());
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
>
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
)) {
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
));
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;
167 LOG(("ScriptLoadReques: inflate FAILED (%s)", zstream
.msg
));
176 } // namespace JS::loader