Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/lib/BCGen/HBC/SerializedLiteralGenerator.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#include "hermes/BCGen/HBC/SerializedLiteralGenerator.h"
9
#include "hermes/BCGen/HBC/BytecodeGenerator.h"
10
#include "llvh/Support/Endian.h"
11
12
namespace hermes {
13
namespace hbc {
14
15
namespace {
16
void appendTagToBuffer(
17
    std::vector<unsigned char> &buff,
18
    SerializedLiteralGenerator::TagType tag,
19
77
    int seqLength) {
20
77
  if (seqLength > 15) {
21
12
    buff.push_back((tag | 0x80) | (seqLength >> 8));
22
12
    buff.push_back(seqLength & 0xffff);
23
65
  } else {
24
65
    buff.push_back(tag + seqLength);
25
65
  }
26
77
}
27
28
/// Write a value to a vector of characters in little-endian format.
29
template <typename value_type>
30
void serializeValueToBuffer(
31
    value_type value,
32
33.3k
    std::vector<unsigned char> &buff) {
33
  // Since endian::write takes a pointer, we can write directly to
34
  // the buffer. First we resize it to make sure the data fits, then
35
  // we pass a pointer to the original end of the vector.
36
  // Since the buffer is a char buffer, we pass in an alignment of 1
37
  // to endian::write.
38
33.3k
  buff.resize(buff.size() + sizeof(value_type));
39
33.3k
  llvh::support::endian::write<value_type, 1>(
40
33.3k
      buff.data() + buff.size() - sizeof(value_type),
41
33.3k
      value,
42
33.3k
      llvh::support::endianness::little);
43
33.3k
}
SerializedLiteralGenerator.cpp:void hermes::hbc::(anonymous namespace)::serializeValueToBuffer<unsigned int>(unsigned int, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >&)
Line
Count
Source
32
33.3k
    std::vector<unsigned char> &buff) {
33
  // Since endian::write takes a pointer, we can write directly to
34
  // the buffer. First we resize it to make sure the data fits, then
35
  // we pass a pointer to the original end of the vector.
36
  // Since the buffer is a char buffer, we pass in an alignment of 1
37
  // to endian::write.
38
33.3k
  buff.resize(buff.size() + sizeof(value_type));
39
33.3k
  llvh::support::endian::write<value_type, 1>(
40
33.3k
      buff.data() + buff.size() - sizeof(value_type),
41
33.3k
      value,
42
33.3k
      llvh::support::endianness::little);
43
33.3k
}
Unexecuted instantiation: SerializedLiteralGenerator.cpp:void hermes::hbc::(anonymous namespace)::serializeValueToBuffer<unsigned short>(unsigned short, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >&)
Unexecuted instantiation: SerializedLiteralGenerator.cpp:void hermes::hbc::(anonymous namespace)::serializeValueToBuffer<unsigned char>(unsigned char, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >&)
44
45
template <>
46
2
void serializeValueToBuffer(double value, std::vector<unsigned char> &buff) {
47
  // Since endian::write takes a pointer, we can write directly to
48
  // the buffer. First we resize it to make sure the data fits, then
49
  // we pass a pointer to the original end of the vector.
50
  // Since the buffer is a char buffer, we pass in an alignment of 1
51
  // to endian::write.
52
2
  buff.resize(buff.size() + sizeof(double));
53
  // NaN may have a different bit pattern on different platforms.
54
  // We need to make sure that the bit pattern is consistent.
55
2
  if (LLVM_UNLIKELY(std::isnan(value))) {
56
0
    static_assert(
57
0
        sizeof(double) == sizeof(uint64_t), "double better be 64 bits");
58
0
    static constexpr uint64_t qnan = 0xfff8000000000000;
59
0
    llvh::support::endian::write<uint64_t, 1>(
60
0
        buff.data() + buff.size() - sizeof(uint64_t),
61
0
        qnan,
62
0
        llvh::support::endianness::little);
63
2
  } else {
64
2
    llvh::support::endian::write<double, 1>(
65
2
        buff.data() + buff.size() - sizeof(double),
66
2
        value,
67
2
        llvh::support::endianness::little);
68
2
  }
69
2
}
70
} // namespace
71
72
void SerializedLiteralGenerator::serializeBuffer(
73
    llvh::ArrayRef<Literal *> literals,
74
    std::vector<unsigned char> &buff,
75
67
    bool isKeyBuffer) {
76
  // Stores the last type parsed by the for loop
77
67
  TagType lastTag = NumberTag;
78
79
  // Stores the type currently being parsed by the for loop
80
67
  TagType newTag = NumberTag;
81
82
  // Stores the values of each serialized Literal in a sequence so that
83
  // they can be added to tempBuff after the tag is finalized
84
67
  std::vector<unsigned char> tmpSeqBuffer;
85
86
  // Stores the length of the current type sequence
87
67
  size_t seqLength = 0;
88
89
33.4k
  for (size_t i = 0, buffSize = literals.size(); i < buffSize; ++i) {
90
    // The first switch simply sets newTag.
91
    // If newTag is different from lastTag, we append the tag and the
92
    // tmpSeqBuffer to buff.
93
    // After this check, we serialize the value to tmpSeqBuffer if possible.
94
    // Since we can only write the tmpSeqBuffer to buff once we know the
95
    // sequence ends, the check has to go after the type checking switch.
96
    // Since we can only write to tmpSeqBuffer after it's been cleared (if
97
    // the sequence is over), the only way to delay the write to tmpSeqBuffer
98
    // is to either write to a second temporary buffer, and write that to
99
    // tmpSeqBuffer afterwards, or add a second switch.
100
33.3k
    switch (literals[i]->getKind()) {
101
33.3k
      case ValueKind::LiteralNumberKind:
102
33.3k
        newTag = llvh::cast<LiteralNumber>(literals[i])
103
33.3k
                     ->isIntTypeRepresentible<int32_t>()
104
33.3k
            ? IntegerTag
105
33.3k
            : NumberTag;
106
33.3k
        break;
107
0
      case ValueKind::LiteralStringKind: {
108
0
        auto str = llvh::cast<LiteralString>(literals[i])->getValue().str();
109
0
        int ind =
110
0
            isKeyBuffer ? BMGen_.getIdentifierID(str) : BMGen_.getStringID(str);
111
112
0
        if (ind > UINT16_MAX) {
113
0
          newTag = LongStringTag;
114
0
        } else if (ind > UINT8_MAX) {
115
0
          newTag = ShortStringTag;
116
0
        } else {
117
0
          newTag = ByteStringTag;
118
0
        }
119
0
        break;
120
0
      }
121
0
      case ValueKind::LiteralBoolKind:
122
0
        newTag = llvh::cast<LiteralBool>(literals[i])->getValue() ? TrueTag
123
0
                                                                  : FalseTag;
124
0
        break;
125
0
      case ValueKind::LiteralNullKind:
126
0
        newTag = NullTag;
127
0
        break;
128
0
      default:
129
0
        hermes_fatal("Invalid Literal Kind");
130
33.3k
    }
131
132
33.3k
    if (newTag != lastTag || seqLength == SequenceMax) {
133
16
      if (seqLength > 0) {
134
10
        appendTagToBuffer(buff, lastTag, seqLength);
135
10
        buff.insert(buff.end(), tmpSeqBuffer.begin(), tmpSeqBuffer.end());
136
10
        tmpSeqBuffer.resize(0);
137
10
      }
138
16
      lastTag = newTag;
139
16
      seqLength = 0;
140
16
    }
141
33.3k
    seqLength++;
142
143
33.3k
    switch (literals[i]->getKind()) {
144
33.3k
      case ValueKind::LiteralNumberKind: {
145
33.3k
        auto litNum = llvh::cast<LiteralNumber>(literals[i]);
146
33.3k
        if (llvh::Optional<int> intPointer =
147
33.3k
                litNum->isIntTypeRepresentible<int32_t>()) {
148
33.3k
          int n = intPointer.getValue();
149
33.3k
          serializeValueToBuffer<uint32_t>(n, tmpSeqBuffer);
150
33.3k
        } else {
151
2
          double num = litNum->getValue();
152
2
          serializeValueToBuffer<double>(num, tmpSeqBuffer);
153
2
        }
154
33.3k
        break;
155
0
      }
156
0
      case ValueKind::LiteralStringKind: {
157
        // For strings, we are going to store the index to the string table,
158
        // which will need to be decoded at runtime.
159
0
        auto str = llvh::cast<LiteralString>(literals[i])->getValue().str();
160
0
        auto stringID =
161
0
            isKeyBuffer ? BMGen_.getIdentifierID(str) : BMGen_.getStringID(str);
162
163
0
        if (stringID > UINT16_MAX) {
164
0
          serializeValueToBuffer<uint32_t>(stringID, tmpSeqBuffer);
165
0
        } else if (stringID > UINT8_MAX) {
166
0
          serializeValueToBuffer<uint16_t>(stringID, tmpSeqBuffer);
167
0
        } else {
168
0
          serializeValueToBuffer<uint8_t>(stringID, tmpSeqBuffer);
169
0
        }
170
0
        break;
171
0
      }
172
0
      case ValueKind::LiteralBoolKind:
173
0
      case ValueKind::LiteralNullKind:
174
        /* no-op */
175
0
        break;
176
0
      default:
177
0
        hermes_fatal("Invalid Literal Kind");
178
33.3k
    }
179
33.3k
  }
180
  // The last value in the buffer can't get serialized in the loop.
181
67
  appendTagToBuffer(buff, lastTag, seqLength);
182
67
  buff.insert(buff.end(), tmpSeqBuffer.begin(), tmpSeqBuffer.end());
183
67
}
184
185
} // namespace hbc
186
} // namespace hermes