Coverage Report

Created: 2025-06-24 06:43

/src/hermes/lib/BCGen/HBC/BytecodeStream.cpp
Line
Count
Source (jump to first uncovered line)
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/BytecodeStream.h"
9
10
using namespace hermes;
11
using namespace hbc;
12
13
// ============================ File ============================
14
0
void BytecodeSerializer::serialize(BytecodeModule &BM, const SHA1 &sourceHash) {
15
0
  bytecodeModule_ = &BM;
16
0
  uint32_t cjsModuleCount = BM.getBytecodeOptions().cjsModulesStaticallyResolved
17
0
      ? BM.getCJSModuleTableStatic().size()
18
0
      : BM.getCJSModuleTable().size();
19
0
  BytecodeFileHeader header{
20
0
      MAGIC,
21
0
      BYTECODE_VERSION,
22
0
      sourceHash,
23
0
      fileLength_,
24
0
      BM.getGlobalFunctionIndex(),
25
0
      BM.getNumFunctions(),
26
0
      static_cast<uint32_t>(BM.getStringKinds().size()),
27
0
      BM.getIdentifierCount(),
28
0
      BM.getStringTableSize(),
29
0
      overflowStringEntryCount_,
30
0
      BM.getStringStorageSize(),
31
0
      static_cast<uint32_t>(BM.getBigIntTable().size()),
32
0
      static_cast<uint32_t>(BM.getBigIntStorage().size()),
33
0
      static_cast<uint32_t>(BM.getRegExpTable().size()),
34
0
      static_cast<uint32_t>(BM.getRegExpStorage().size()),
35
0
      BM.getArrayBufferSize(),
36
0
      BM.getObjectKeyBufferSize(),
37
0
      BM.getObjectValueBufferSize(),
38
0
      BM.getSegmentID(),
39
0
      cjsModuleCount,
40
0
      static_cast<uint32_t>(BM.getFunctionSourceTable().size()),
41
0
      debugInfoOffset_,
42
0
      BM.getBytecodeOptions()};
43
0
  writeBinary(header);
44
  // Sizes of file and function headers are tuned for good cache line packing.
45
  // If you reorder the format, try to avoid headers crossing cache lines.
46
0
  visitBytecodeSegmentsInOrder(*this);
47
0
  serializeFunctionsBytecode(BM);
48
49
0
  for (auto &entry : BM.getFunctionTable()) {
50
0
    serializeFunctionInfo(*entry);
51
0
  }
52
53
0
  serializeDebugInfo(BM);
54
55
0
  SHA1 fileHash{};
56
0
  if (!isLayout_) {
57
0
    auto hash = outputHasher_.result();
58
0
    assert(hash.size() == sizeof(fileHash) && "Incorrect length of SHA1 hash");
59
0
    std::copy(hash.begin(), hash.end(), fileHash.begin());
60
0
  }
61
  // Even in layout mode, we "write" a footer (with an ignored zero hash),
62
  // so that fileLength_ is set correctly.
63
0
  writeBinary(BytecodeFileFooter{fileHash});
64
65
0
  if (isLayout_) {
66
0
    finishLayout(BM);
67
0
    serialize(BM, sourceHash);
68
0
  }
69
0
}
70
71
0
void BytecodeSerializer::finishLayout(BytecodeModule &BM) {
72
0
  fileLength_ = loc_;
73
0
  assert(fileLength_ > 0 && "Empty file after layout");
74
0
  isLayout_ = false;
75
0
  loc_ = 0;
76
0
}
77
78
// ========================== Function Table ==========================
79
0
void BytecodeSerializer::serializeFunctionTable(BytecodeModule &BM) {
80
0
  for (auto &entry : BM.getFunctionTable()) {
81
0
    if (options_.stripDebugInfoSection) {
82
      // Change flag on the actual BF, so it's seen by serializeFunctionInfo.
83
0
      entry->mutableFlags().hasDebugInfo = false;
84
0
    }
85
0
    FunctionHeader header = entry->getHeader();
86
0
    writeBinary(SmallFuncHeader(header));
87
0
  }
88
0
}
89
90
// ========================== DebugInfo ==========================
91
0
void BytecodeSerializer::serializeDebugInfo(BytecodeModule &BM) {
92
0
  pad(BYTECODE_ALIGNMENT);
93
0
  const DebugInfo &info = BM.getDebugInfo();
94
0
  debugInfoOffset_ = loc_;
95
96
0
  if (options_.stripDebugInfoSection) {
97
0
    const DebugInfoHeader empty = {0, 0, 0, 0, 0, 0, 0};
98
0
    writeBinary(empty);
99
0
    return;
100
0
  }
101
102
0
  const llvh::ArrayRef<StringTableEntry> filenameTable =
103
0
      info.getFilenameTable();
104
0
  const auto filenameStorage = info.getFilenameStorage();
105
0
  const DebugInfo::DebugFileRegionList &files = info.viewFiles();
106
0
  const StreamVector<uint8_t> &data = info.viewData();
107
0
  uint32_t scopeDescOffset = info.scopeDescDataOffset();
108
0
  uint32_t tCalleeOffset = info.textifiedCalleeOffset();
109
0
  uint32_t stOffset = info.stringTableOffset();
110
111
0
  DebugInfoHeader header{
112
0
      (uint32_t)filenameTable.size(),
113
0
      (uint32_t)filenameStorage.size(),
114
0
      (uint32_t)files.size(),
115
0
      scopeDescOffset,
116
0
      tCalleeOffset,
117
0
      stOffset,
118
0
      (uint32_t)data.size()};
119
0
  writeBinary(header);
120
0
  writeBinaryArray(filenameTable);
121
0
  writeBinaryArray(filenameStorage);
122
0
  for (auto &file : files) {
123
0
    writeBinary(file);
124
0
  }
125
0
  writeBinaryArray(data.getData());
126
0
}
127
128
// ===================== CommonJS Module Table ======================
129
0
void BytecodeSerializer::serializeCJSModuleTable(BytecodeModule &BM) {
130
0
  pad(BYTECODE_ALIGNMENT);
131
132
0
  for (const auto &it : BM.getCJSModuleTable()) {
133
0
    writeBinary(it.first);
134
0
    writeBinary(it.second);
135
0
  }
136
137
0
  writeBinaryArray(BM.getCJSModuleTableStatic());
138
0
}
139
140
// ===================== Function Source Table ======================
141
0
void BytecodeSerializer::serializeFunctionSourceTable(BytecodeModule &BM) {
142
0
  pad(BYTECODE_ALIGNMENT);
143
144
0
  writeBinaryArray(BM.getFunctionSourceTable());
145
0
}
146
147
// ==================== Exception Handler Table =====================
148
0
void BytecodeSerializer::serializeExceptionHandlerTable(BytecodeFunction &BF) {
149
0
  if (!BF.hasExceptionHandlers())
150
0
    return;
151
152
0
  pad(INFO_ALIGNMENT);
153
0
  ExceptionHandlerTableHeader header{BF.getExceptionHandlerCount()};
154
0
  writeBinary(header);
155
156
0
  writeBinaryArray(BF.getExceptionHandlers());
157
0
}
158
159
// ========================= Array Buffer ==========================
160
0
void BytecodeSerializer::serializeArrayBuffer(BytecodeModule &BM) {
161
0
  writeBinaryArray(BM.getArrayBuffer());
162
0
}
163
164
0
void BytecodeSerializer::serializeObjectBuffer(BytecodeModule &BM) {
165
0
  auto objectKeyValBufferPair = BM.getObjectBuffer();
166
167
0
  writeBinaryArray(objectKeyValBufferPair.first);
168
0
  writeBinaryArray(objectKeyValBufferPair.second);
169
0
}
170
171
0
void BytecodeSerializer::serializeDebugOffsets(BytecodeFunction &BF) {
172
0
  if (options_.stripDebugInfoSection || !BF.hasDebugInfo()) {
173
0
    return;
174
0
  }
175
176
0
  pad(INFO_ALIGNMENT);
177
0
  auto *offsets = BF.getDebugOffsets();
178
0
  writeBinary(*offsets);
179
0
}
180
181
// ============================ Function ============================
182
0
void BytecodeSerializer::serializeFunctionsBytecode(BytecodeModule &BM) {
183
  // Map from opcodes and jumptables to offsets, used to deduplicate bytecode.
184
0
  using DedupKey = llvh::ArrayRef<opcode_atom_t>;
185
0
  llvh::DenseMap<DedupKey, uint32_t> bcMap;
186
0
  for (auto &entry : BM.getFunctionTable()) {
187
0
    if (options_.optimizationEnabled) {
188
      // If identical bytecode exists, we'll reuse it.
189
0
      bool reuse = false;
190
0
      if (isLayout_) {
191
        // Deduplicate the bytecode during layout phase.
192
0
        DedupKey key = entry->getOpcodeArray();
193
0
        auto pair =
194
0
            bcMap.insert(std::make_pair(key, static_cast<uint32_t>(loc_)));
195
0
        if (!pair.second) {
196
0
          reuse = true;
197
0
          entry->setOffset(pair.first->second);
198
0
        }
199
0
      } else {
200
        // Cheaply determine whether bytecode was deduplicated.
201
0
        assert(entry->getOffset() && "Function lacks offset after layout");
202
0
        assert(entry->getOffset() <= loc_ && "Function has too large offset");
203
0
        reuse = entry->getOffset() < loc_;
204
0
      }
205
0
      if (reuse) {
206
0
        continue;
207
0
      }
208
0
    }
209
210
    // Set the offset of this function's bytecode.
211
0
    if (isLayout_) {
212
0
      entry->setOffset(loc_);
213
0
    }
214
215
    // Serialize opcodes.
216
0
    writeBinaryArray(entry->getOpcodesOnly());
217
218
    // Serialize any jump table after the opcode block.
219
0
    if (!entry->getJumpTablesOnly().empty()) {
220
0
      pad(sizeof(uint32_t));
221
0
      writeBinaryArray(entry->getJumpTablesOnly());
222
0
    }
223
0
    if (options_.padFunctionBodiesPercent) {
224
0
      size_t size = entry->getOpcodesOnly().size();
225
0
      size = (size * options_.padFunctionBodiesPercent) / 100;
226
0
      while (size--)
227
0
        writeBinary('\0');
228
0
      pad(sizeof(uint32_t));
229
0
    }
230
0
  }
231
0
}
232
233
0
void BytecodeSerializer::serializeFunctionInfo(BytecodeFunction &BF) {
234
  // Set the offset of this function's info. Any subsection that is present is
235
  // aligned to INFO_ALIGNMENT, so we also align the recorded offset to that.
236
0
  if (isLayout_) {
237
0
    BF.setInfoOffset(llvh::alignTo(loc_, INFO_ALIGNMENT));
238
0
  }
239
240
  // Write large header if it doesn't fit in a small.
241
0
  FunctionHeader header = BF.getHeader();
242
0
  if (SmallFuncHeader(header).flags.overflowed) {
243
0
    pad(INFO_ALIGNMENT);
244
0
    writeBinary(header);
245
0
  }
246
247
  // Serialize exception handlers.
248
0
  serializeExceptionHandlerTable(BF);
249
250
  // Add offset in debug info (if function has debug info).
251
0
  serializeDebugOffsets(BF);
252
0
}
253
254
0
void BytecodeSerializer::visitFunctionHeaders() {
255
0
  pad(BYTECODE_ALIGNMENT);
256
0
  serializeFunctionTable(*bytecodeModule_);
257
0
}
258
259
0
void BytecodeSerializer::visitStringKinds() {
260
0
  pad(BYTECODE_ALIGNMENT);
261
0
  writeBinaryArray(bytecodeModule_->getStringKinds());
262
0
}
263
264
0
void BytecodeSerializer::visitIdentifierHashes() {
265
0
  pad(BYTECODE_ALIGNMENT);
266
0
  writeBinaryArray(bytecodeModule_->getIdentifierHashes());
267
0
}
268
269
0
void BytecodeSerializer::visitSmallStringTable() {
270
0
  pad(BYTECODE_ALIGNMENT);
271
0
  uint32_t overflowCount = 0;
272
0
  for (auto &entry : bytecodeModule_->getStringTable()) {
273
0
    SmallStringTableEntry small(entry, overflowCount);
274
0
    writeBinary(small);
275
0
    overflowCount += small.isOverflowed();
276
0
  }
277
0
  overflowStringEntryCount_ = overflowCount;
278
0
}
279
280
0
void BytecodeSerializer::visitOverflowStringTable() {
281
0
  pad(BYTECODE_ALIGNMENT);
282
0
  llvh::SmallVector<OverflowStringTableEntry, 64> overflow;
283
0
  for (auto &entry : bytecodeModule_->getStringTable()) {
284
0
    SmallStringTableEntry small(entry, overflow.size());
285
0
    if (small.isOverflowed()) {
286
0
      overflow.emplace_back(entry.getOffset(), entry.getLength());
287
0
    }
288
0
  }
289
0
  writeBinaryArray(llvh::makeArrayRef(overflow));
290
0
}
291
292
0
void BytecodeSerializer::visitStringStorage() {
293
0
  pad(BYTECODE_ALIGNMENT);
294
0
  writeBinaryArray(bytecodeModule_->getStringStorage());
295
0
}
296
297
0
void BytecodeSerializer::visitArrayBuffer() {
298
0
  pad(BYTECODE_ALIGNMENT);
299
0
  serializeArrayBuffer(*bytecodeModule_);
300
0
}
301
302
0
void BytecodeSerializer::visitObjectKeyBuffer() {
303
0
  pad(BYTECODE_ALIGNMENT);
304
0
  auto objectKeyValBufferPair = bytecodeModule_->getObjectBuffer();
305
0
  writeBinaryArray(objectKeyValBufferPair.first);
306
0
}
307
308
0
void BytecodeSerializer::visitObjectValueBuffer() {
309
0
  pad(BYTECODE_ALIGNMENT);
310
0
  auto objectKeyValBufferPair = bytecodeModule_->getObjectBuffer();
311
0
  writeBinaryArray(objectKeyValBufferPair.second);
312
0
}
313
314
0
void BytecodeSerializer::visitBigIntTable() {
315
0
  pad(BYTECODE_ALIGNMENT);
316
0
  writeBinaryArray(bytecodeModule_->getBigIntTable());
317
0
}
318
319
0
void BytecodeSerializer::visitBigIntStorage() {
320
0
  pad(BYTECODE_ALIGNMENT);
321
0
  writeBinaryArray(bytecodeModule_->getBigIntStorage());
322
0
}
323
324
0
void BytecodeSerializer::visitRegExpTable() {
325
0
  pad(BYTECODE_ALIGNMENT);
326
0
  writeBinaryArray(bytecodeModule_->getRegExpTable());
327
0
}
328
329
0
void BytecodeSerializer::visitRegExpStorage() {
330
0
  pad(BYTECODE_ALIGNMENT);
331
0
  writeBinaryArray(bytecodeModule_->getRegExpStorage());
332
0
}
333
334
0
void BytecodeSerializer::visitCJSModuleTable() {
335
0
  pad(BYTECODE_ALIGNMENT);
336
0
  serializeCJSModuleTable(*bytecodeModule_);
337
0
}
338
339
0
void BytecodeSerializer::visitFunctionSourceTable() {
340
0
  pad(BYTECODE_ALIGNMENT);
341
0
  serializeFunctionSourceTable(*bytecodeModule_);
342
0
}