tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ScriptCompression.cpp (6489B)


      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/. */
      6 
      7 #include "ScriptLoadRequest.h"
      8 #include "ScriptLoader.h"
      9 #include "mozilla/PerfStats.h"
     10 #include "mozilla/Preferences.h"
     11 #include "mozilla/ProfilerMarkers.h"
     12 #include "mozilla/ScopeExit.h"
     13 #include "mozilla/StaticPrefs_browser.h"
     14 #include "mozilla/Vector.h"
     15 #include "zlib.h"
     16 
     17 using namespace mozilla;
     18 
     19 namespace JS::loader {
     20 
     21 #undef LOG
     22 #define LOG(args)                                       \
     23  MOZ_LOG(mozilla::dom::ScriptLoader::gScriptLoaderLog, \
     24          mozilla::LogLevel::Debug, args)
     25 
     26 /*
     27 * ScriptBytecodeDataLayout
     28 *
     29 * ScriptBytecodeDataLayout provides accessors to maintain the correct data
     30 * layout of the ScriptLoadRequest's script bytecode buffer.
     31 */
     32 class ScriptBytecodeDataLayout {
     33 public:
     34  explicit ScriptBytecodeDataLayout(mozilla::Vector<uint8_t>& aBytecode,
     35                                    size_t aBytecodeOffset)
     36      : mBytecode(aBytecode), mBytecodeOffset(aBytecodeOffset) {}
     37 
     38  uint8_t* prelude() const { return mBytecode.begin(); }
     39  size_t preludeLength() const { return mBytecodeOffset; }
     40 
     41  uint8_t* bytecode() const { return prelude() + mBytecodeOffset; }
     42  size_t bytecodeLength() const { return mBytecode.length() - preludeLength(); }
     43 
     44  mozilla::Vector<uint8_t>& mBytecode;
     45  size_t mBytecodeOffset;
     46 };
     47 
     48 /*
     49 * ScriptBytecodeCompressedDataLayout
     50 *
     51 * ScriptBytecodeCompressedDataLayout provides accessors to maintain the correct
     52 * data layout of a compressed script bytecode buffer.
     53 */
     54 class ScriptBytecodeCompressedDataLayout {
     55 public:
     56  using UncompressedLengthType = uint32_t;
     57 
     58  explicit ScriptBytecodeCompressedDataLayout(
     59      mozilla::Vector<uint8_t>& aBytecode, size_t aBytecodeOffset)
     60      : mBytecode(aBytecode), mBytecodeOffset(aBytecodeOffset) {}
     61 
     62  uint8_t* prelude() const { return mBytecode.begin(); }
     63  size_t preludeLength() const { return mBytecodeOffset; }
     64 
     65  uint8_t* uncompressedLength() const { return prelude() + mBytecodeOffset; }
     66  size_t uncompressedLengthLength() const {
     67    return sizeof(UncompressedLengthType);
     68  }
     69 
     70  uint8_t* bytecode() const {
     71    return uncompressedLength() + uncompressedLengthLength();
     72  }
     73  size_t bytecodeLength() const {
     74    return mBytecode.length() - uncompressedLengthLength() - preludeLength();
     75  }
     76 
     77  mozilla::Vector<uint8_t>& mBytecode;
     78  size_t mBytecodeOffset;
     79 };
     80 
     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
     85 
     86  AUTO_PROFILER_MARKER_UNTYPED("ScriptBytecodeCompress", JS, {});
     87  PerfStats::AutoMetricRecording<PerfStats::Metric::JSBC_Compression>
     88      autoRecording;
     89 
     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};
     97 
     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;
    106  }
    107  auto autoDestroy = MakeScopeExit([&]() { deflateEnd(&zstream); });
    108 
    109  auto compressedLength = deflateBound(&zstream, uncompressedLength);
    110  if (!aCompressedBytecodeBufOut.resizeUninitialized(
    111          compressedLength + compressedLayout.preludeLength() +
    112          compressedLayout.uncompressedLengthLength())) {
    113    return false;
    114  }
    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;
    121 
    122  int ret = deflate(&zstream, Z_FINISH);
    123  if (ret == Z_MEM_ERROR) {
    124    return false;
    125  }
    126  MOZ_RELEASE_ASSERT(ret == Z_STREAM_END);
    127 
    128  aCompressedBytecodeBufOut.shrinkTo(zstream.next_out -
    129                                     aCompressedBytecodeBufOut.begin());
    130  return true;
    131 }
    132 
    133 bool ScriptBytecodeDecompress(Vector<uint8_t>& aCompressedBytecodeBuf,
    134                              size_t aBytecodeOffset,
    135                              Vector<uint8_t>& aBytecodeBufOut) {
    136  AUTO_PROFILER_MARKER_UNTYPED("ScriptBytecodeDecompress", JS, {});
    137  PerfStats::AutoMetricRecording<PerfStats::Metric::JSBC_Decompression>
    138      autoRecording;
    139 
    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;
    149  }
    150  memcpy(uncompressedLayout.prelude(), compressedLayout.prelude(),
    151         compressedLayout.preludeLength());
    152 
    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;
    161  }
    162  auto autoDestroy = MakeScopeExit([&]() { inflateEnd(&zstream); });
    163 
    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;
    169  }
    170 
    171  return true;
    172 }
    173 
    174 #undef LOG
    175 
    176 }  // namespace JS::loader