Coverage Report

Created: 2025-08-28 06:48

/src/hermes/lib/VM/RuntimeModule.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/VM/RuntimeModule.h"
9
10
#include "hermes/BCGen/HBC/BytecodeProviderFromSrc.h"
11
#include "hermes/Support/PerfSection.h"
12
#include "hermes/VM/CodeBlock.h"
13
#include "hermes/VM/Domain.h"
14
#include "hermes/VM/HiddenClass.h"
15
#include "hermes/VM/Predefined.h"
16
#include "hermes/VM/Runtime.h"
17
#include "hermes/VM/RuntimeModule-inline.h"
18
#include "hermes/VM/StringPrimitive.h"
19
#include "hermes/VM/WeakRoot-inline.h"
20
21
namespace hermes {
22
namespace vm {
23
24
RuntimeModule::RuntimeModule(
25
    Runtime &runtime,
26
    Handle<Domain> domain,
27
    RuntimeModuleFlags flags,
28
    llvh::StringRef sourceURL,
29
    facebook::hermes::debugger::ScriptID scriptID)
30
262
    : runtime_(runtime),
31
262
      domain_(*domain, runtime),
32
262
      flags_(flags),
33
262
      sourceURL_(sourceURL),
34
262
      scriptID_(scriptID) {
35
262
  runtime_.addRuntimeModule(this);
36
262
  Domain::addRuntimeModule(domain, runtime, this);
37
262
#ifndef HERMESVM_LEAN
38
262
  lazyRoot_ = this;
39
262
#endif
40
262
}
41
42
SymbolID RuntimeModule::createSymbolFromStringIDMayAllocate(
43
    StringID stringID,
44
    const StringTableEntry &entry,
45
13.6k
    OptValue<uint32_t> mhash) {
46
  // Use manual pointer arithmetic to avoid out of bounds errors on empty
47
  // string accesses.
48
13.6k
  auto strStorage = bcProvider_->getStringStorage();
49
13.6k
  if (entry.isUTF16()) {
50
236
    const char16_t *s =
51
236
        (const char16_t *)(strStorage.begin() + entry.getOffset());
52
236
    UTF16Ref str{s, entry.getLength()};
53
236
    uint32_t hash = mhash ? *mhash : hashString(str);
54
236
    return mapStringMayAllocate(str, stringID, hash);
55
13.4k
  } else {
56
    // ASCII.
57
13.4k
    const char *s = (const char *)strStorage.begin() + entry.getOffset();
58
13.4k
    ASCIIRef str{s, entry.getLength()};
59
13.4k
    uint32_t hash = mhash ? *mhash : hashString(str);
60
13.4k
    return mapStringMayAllocate(str, stringID, hash);
61
13.4k
  }
62
13.6k
}
63
64
262
RuntimeModule::~RuntimeModule() {
65
262
  if (bcProvider_ && !bcProvider_->getRawBuffer().empty())
66
186
    runtime_.getCrashManager().unregisterMemory(bcProvider_.get());
67
262
  runtime_.getCrashManager().unregisterMemory(this);
68
262
  runtime_.removeRuntimeModule(this);
69
70
  // We may reference other CodeBlocks through lazy compilation, but we only
71
  // own the ones that reference us.
72
9.67k
  for (auto *block : functionMap_) {
73
9.67k
    if (block != nullptr && block->getRuntimeModule() == this) {
74
3.71k
      runtime_.getHeap().getIDTracker().untrackNative(block);
75
3.71k
      delete block;
76
3.71k
    }
77
9.67k
  }
78
262
  runtime_.getHeap().getIDTracker().untrackNative(&functionMap_);
79
262
}
80
81
262
void RuntimeModule::prepareForDestruction() {
82
9.94k
  for (int i = 0, e = functionMap_.size(); i < e; i++) {
83
9.67k
    if (functionMap_[i] != nullptr &&
84
9.67k
        functionMap_[i]->getRuntimeModule() != this) {
85
6
      functionMap_[i] = nullptr;
86
6
    }
87
9.67k
  }
88
262
}
89
90
CallResult<RuntimeModule *> RuntimeModule::create(
91
    Runtime &runtime,
92
    Handle<Domain> domain,
93
    facebook::hermes::debugger::ScriptID scriptID,
94
    std::shared_ptr<hbc::BCProvider> &&bytecode,
95
    RuntimeModuleFlags flags,
96
163
    llvh::StringRef sourceURL) {
97
163
  RuntimeModule *result;
98
163
  result = new RuntimeModule(runtime, domain, flags, sourceURL, scriptID);
99
163
  runtime.getCrashManager().registerMemory(result, sizeof(*result));
100
163
  if (bytecode) {
101
163
    if (result->initializeMayAllocate(std::move(bytecode)) ==
102
163
        ExecutionStatus::EXCEPTION) {
103
0
      return ExecutionStatus::EXCEPTION;
104
0
    }
105
    // If the BC provider is backed by a buffer, register the BC provider struct
106
    // (but not the buffer contents, since that might be too large).
107
163
    if (result->bcProvider_ && !result->bcProvider_->getRawBuffer().empty())
108
93
      runtime.getCrashManager().registerMemory(
109
93
          result->bcProvider_.get(), sizeof(hbc::BCProviderFromBuffer));
110
163
  }
111
163
  return result;
112
163
}
113
114
RuntimeModule *RuntimeModule::createUninitialized(
115
    Runtime &runtime,
116
    Handle<Domain> domain,
117
    RuntimeModuleFlags flags,
118
99
    facebook::hermes::debugger::ScriptID scriptID) {
119
99
  return new RuntimeModule(runtime, domain, flags, "", scriptID);
120
99
}
121
122
void RuntimeModule::initializeWithoutCJSModulesMayAllocate(
123
256
    std::shared_ptr<hbc::BCProvider> &&bytecode) {
124
256
  assert(!bcProvider_ && "RuntimeModule already initialized");
125
256
  bcProvider_ = std::move(bytecode);
126
256
  importStringIDMapMayAllocate();
127
256
  initializeFunctionMap();
128
256
}
129
130
ExecutionStatus RuntimeModule::initializeMayAllocate(
131
163
    std::shared_ptr<hbc::BCProvider> &&bytecode) {
132
163
  initializeWithoutCJSModulesMayAllocate(std::move(bytecode));
133
163
  if (LLVM_UNLIKELY(importCJSModuleTable() == ExecutionStatus::EXCEPTION)) {
134
0
    return ExecutionStatus::EXCEPTION;
135
0
  }
136
163
  return ExecutionStatus::RETURNED;
137
163
}
138
139
3.71k
CodeBlock *RuntimeModule::getCodeBlockSlowPath(unsigned index) {
140
3.71k
#ifndef HERMESVM_LEAN
141
3.71k
  if (bcProvider_->isFunctionLazy(index)) {
142
6
    auto *lazyModule = RuntimeModule::createLazyModule(
143
6
        runtime_, getDomain(runtime_), this, index);
144
6
    functionMap_[index] = lazyModule->getOnlyLazyCodeBlock();
145
6
    return functionMap_[index];
146
6
  }
147
3.71k
#endif
148
3.71k
  functionMap_[index] = CodeBlock::createCodeBlock(
149
3.71k
      this,
150
3.71k
      bcProvider_->getFunctionHeader(index),
151
3.71k
      bcProvider_->getBytecode(index),
152
3.71k
      index);
153
3.71k
  return functionMap_[index];
154
3.71k
}
155
156
#ifndef HERMESVM_LEAN
157
RuntimeModule *RuntimeModule::createLazyModule(
158
    Runtime &runtime,
159
    Handle<Domain> domain,
160
    RuntimeModule *parent,
161
6
    uint32_t functionID) {
162
6
  auto RM = createUninitialized(runtime, domain);
163
6
  RM->lazyRoot_ = parent->lazyRoot_;
164
  // Copy the lazy root's script ID for lazy modules.
165
6
  RM->scriptID_ = RM->lazyRoot_->scriptID_;
166
167
  // Set the bcProvider's BytecodeModule to point to the parent's.
168
6
  assert(parent->isInitialized() && "Parent module must have been initialized");
169
170
6
  auto *bcFunction = &((hbc::BCProviderFromSrc *)parent->getBytecode())
171
6
                          ->getBytecodeModule()
172
6
                          ->getFunction(functionID);
173
174
6
  RM->bcProvider_ = hbc::BCProviderLazy::createBCProviderLazy(bcFunction);
175
176
  // We don't know which function index this block will eventually represent,
177
  // so just add it as 0 to ensure ownership. We'll move it later in
178
  // `initializeLazy`.
179
6
  RM->functionMap_.emplace_back(CodeBlock::createCodeBlock(
180
6
      RM, RM->bcProvider_->getFunctionHeader(functionID), {}, functionID));
181
182
  // The module doesn't have a string table until we've compiled the block,
183
  // so just add the string name as 0 in the mean time for f.name to work via
184
  // getLazyName(). Since it's in the stringIDMap_, it'll be correctly GC'd.
185
6
  RM->stringIDMap_.emplace_back(parent->getSymbolIDFromStringIDMayAllocate(
186
6
      bcFunction->getHeader().functionName));
187
188
6
  return RM;
189
6
}
190
191
6
SymbolID RuntimeModule::getLazyName() {
192
6
  assert(functionMap_.size() == 1 && "Not a lazy module?");
193
6
  assert(stringIDMap_.size() == 1 && "Missing lazy function name symbol");
194
6
  assert(this->stringIDMap_[0].isValid() && "Invalid function name symbol");
195
6
  return this->stringIDMap_[0];
196
6
}
197
198
void RuntimeModule::initializeLazyMayAllocate(
199
0
    std::unique_ptr<hbc::BCProvider> bytecode) {
200
  // Clear the old data provider first.
201
0
  bcProvider_ = nullptr;
202
203
  // Initialize without CJS module table because this compilation is done
204
  // separately, and the bytecode will not contain a module table.
205
0
  initializeWithoutCJSModulesMayAllocate(std::move(bytecode));
206
207
  // createLazyCodeBlock added a single codeblock as functionMap_[0]
208
0
  assert(functionMap_[0] && "Missing first entry");
209
210
  // We should move it to the index where it's supposed to be. This ensures a
211
  // 1-1 relationship between codeblocks and bytecodefunctions, which the
212
  // debugger relies on for setting step-out breakpoints in all functions.
213
0
  if (bcProvider_->getGlobalFunctionIndex() == 0) {
214
    // No move needed
215
0
    return;
216
0
  }
217
0
  assert(
218
0
      !functionMap_[bcProvider_->getGlobalFunctionIndex()] &&
219
0
      "Entry point is already occupied");
220
0
  functionMap_[bcProvider_->getGlobalFunctionIndex()] = functionMap_[0];
221
0
  functionMap_[0] = nullptr;
222
0
}
223
#endif
224
225
256
void RuntimeModule::importStringIDMapMayAllocate() {
226
256
  assert(bcProvider_ && "Uninitialized RuntimeModule");
227
256
  PerfSection perf("Import String ID Map");
228
256
  GCScope scope(runtime_);
229
230
256
  auto strTableSize = bcProvider_->getStringCount();
231
232
256
  stringIDMap_.clear();
233
234
  // Populate the string ID map with empty identifiers.
235
256
  stringIDMap_.resize(strTableSize, RootSymbolID(SymbolID::empty()));
236
237
256
  if (runtime_.getVMExperimentFlags() & experiments::MAdviseStringsSequential) {
238
0
    bcProvider_->adviseStringTableSequential();
239
0
  }
240
241
256
  if (runtime_.getVMExperimentFlags() & experiments::MAdviseStringsWillNeed) {
242
0
    bcProvider_->willNeedStringTable();
243
0
  }
244
245
  // Get the array of pre-computed hashes from identifiers in the bytecode
246
  // to their runtime representation as SymbolIDs.
247
256
  auto kinds = bcProvider_->getStringKinds();
248
256
  auto hashes = bcProvider_->getIdentifierHashes();
249
256
  assert(
250
256
      hashes.size() <= strTableSize &&
251
256
      "Should not have more strings than identifiers");
252
253
  // Preallocate enough space to store all identifiers to prevent
254
  // unnecessary allocations. NOTE: If this module is not the first module,
255
  // then this is an underestimate.
256
256
  runtime_.getIdentifierTable().reserve(hashes.size());
257
256
  {
258
256
    StringID strID = 0;
259
256
    uint32_t hashID = 0;
260
261
321
    for (auto entry : kinds) {
262
321
      switch (entry.kind()) {
263
163
        case StringKind::String:
264
163
          strID += entry.count();
265
163
          break;
266
267
158
        case StringKind::Identifier:
268
13.0k
          for (uint32_t i = 0; i < entry.count(); ++i, ++strID, ++hashID) {
269
12.8k
            createSymbolFromStringIDMayAllocate(
270
12.8k
                strID, bcProvider_->getStringTableEntry(strID), hashes[hashID]);
271
12.8k
          }
272
158
          break;
273
321
      }
274
321
    }
275
276
256
    assert(strID == strTableSize && "Should map every string in the bytecode.");
277
256
    assert(hashID == hashes.size() && "Should hash all identifiers.");
278
256
  }
279
280
256
  if (runtime_.getVMExperimentFlags() & experiments::MAdviseStringsRandom) {
281
0
    bcProvider_->adviseStringTableRandom();
282
0
  }
283
284
256
  if (strTableSize == 0) {
285
    // If the string table turns out to be empty,
286
    // we always add one empty string to it.
287
    // Note that this can only happen when we are creating the RuntimeModule
288
    // in a non-standard way, either in unit tests or the special
289
    // emptyCodeBlockRuntimeModule_ in Runtime where the creation happens
290
    // manually instead of going through bytecode module generation.
291
    // In those cases, functions will be created with a default nameID=0
292
    // without adding the name string into the string table. Hence here
293
    // we need to add it manually and it will have index 0.
294
93
    ASCIIRef s;
295
93
    stringIDMap_.push_back({});
296
93
    mapStringMayAllocate(s, 0, hashString(s));
297
93
  }
298
256
}
299
300
256
void RuntimeModule::initializeFunctionMap() {
301
256
  assert(bcProvider_ && "Uninitialized RuntimeModule");
302
256
  assert(
303
256
      bcProvider_->getFunctionCount() >= functionMap_.size() &&
304
256
      "Unexpected size reduction. Lazy module missing functions?");
305
256
  functionMap_.resize(bcProvider_->getFunctionCount());
306
256
}
307
308
163
ExecutionStatus RuntimeModule::importCJSModuleTable() {
309
163
  PerfSection perf("Import CJS Module Table");
310
163
  return Domain::importCJSModuleTable(getDomain(runtime_), runtime_, this);
311
163
}
312
313
StringPrimitive *RuntimeModule::getStringPrimFromStringIDMayAllocate(
314
733k
    StringID stringID) {
315
733k
  return runtime_.getStringPrimFromSymbolID(
316
733k
      getSymbolIDFromStringIDMayAllocate(stringID));
317
733k
}
318
319
0
std::string RuntimeModule::getStringFromStringID(StringID stringID) {
320
0
  auto entry = bcProvider_->getStringTableEntry(stringID);
321
0
  auto strStorage = bcProvider_->getStringStorage();
322
0
  if (entry.isUTF16()) {
323
0
    const char16_t *s =
324
0
        (const char16_t *)(strStorage.begin() + entry.getOffset());
325
0
    std::string out;
326
0
    convertUTF16ToUTF8WithReplacements(out, UTF16Ref{s, entry.getLength()});
327
0
    return out;
328
0
  } else {
329
    // ASCII.
330
0
    const char *s = (const char *)strStorage.begin() + entry.getOffset();
331
0
    return std::string{s, entry.getLength()};
332
0
  }
333
0
}
334
335
llvh::ArrayRef<uint8_t> RuntimeModule::getBigIntBytesFromBigIntId(
336
620k
    BigIntID bigIntId) const {
337
620k
  assert(
338
620k
      bigIntId < bcProvider_->getBigIntTable().size() && "Invalid bigint id");
339
620k
  bigint::BigIntTableEntry entry = bcProvider_->getBigIntTable()[bigIntId];
340
620k
  return bcProvider_->getBigIntStorage().slice(entry.offset, entry.length);
341
620k
}
342
343
llvh::ArrayRef<uint8_t> RuntimeModule::getRegExpBytecodeFromRegExpID(
344
208
    uint32_t regExpId) const {
345
208
  assert(
346
208
      regExpId < bcProvider_->getRegExpTable().size() && "Invalid regexp id");
347
208
  RegExpTableEntry entry = bcProvider_->getRegExpTable()[regExpId];
348
208
  return bcProvider_->getRegExpStorage().slice(entry.offset, entry.length);
349
208
}
350
351
template <typename T>
352
SymbolID RuntimeModule::mapStringMayAllocate(
353
    llvh::ArrayRef<T> str,
354
    StringID stringID,
355
13.7k
    uint32_t hash) {
356
  // Create a SymbolID for a given string. In general a SymbolID holds onto an
357
  // intern'd StringPrimitive. As an optimization, if this RuntimeModule is
358
  // persistent, then it will not be deallocated before the Runtime, and we can
359
  // have the SymbolID hold a raw pointer into the storage and produce the
360
  // StringPrimitive when it is first required.
361
13.7k
  SymbolID id;
362
13.7k
  if (flags_.persistent) {
363
    // Registering a lazy identifier does not allocate, so we do not need a
364
    // GC scope.
365
13.6k
    id = runtime_.getIdentifierTable().registerLazyIdentifier(str, hash);
366
13.6k
  } else {
367
    // Accessing a symbol non-lazily may allocate in the GC heap, so add a scope
368
    // marker.
369
124
    GCScopeMarkerRAII scopeMarker{runtime_};
370
124
    id = *runtime_.ignoreAllocationFailure(
371
124
        runtime_.getIdentifierTable().getSymbolHandle(runtime_, str, hash));
372
124
  }
373
13.7k
  stringIDMap_[stringID] = RootSymbolID(id);
374
13.7k
  return id;
375
13.7k
}
hermes::vm::SymbolID hermes::vm::RuntimeModule::mapStringMayAllocate<char16_t>(llvh::ArrayRef<char16_t>, unsigned int, unsigned int)
Line
Count
Source
355
236
    uint32_t hash) {
356
  // Create a SymbolID for a given string. In general a SymbolID holds onto an
357
  // intern'd StringPrimitive. As an optimization, if this RuntimeModule is
358
  // persistent, then it will not be deallocated before the Runtime, and we can
359
  // have the SymbolID hold a raw pointer into the storage and produce the
360
  // StringPrimitive when it is first required.
361
236
  SymbolID id;
362
236
  if (flags_.persistent) {
363
    // Registering a lazy identifier does not allocate, so we do not need a
364
    // GC scope.
365
234
    id = runtime_.getIdentifierTable().registerLazyIdentifier(str, hash);
366
234
  } else {
367
    // Accessing a symbol non-lazily may allocate in the GC heap, so add a scope
368
    // marker.
369
2
    GCScopeMarkerRAII scopeMarker{runtime_};
370
2
    id = *runtime_.ignoreAllocationFailure(
371
2
        runtime_.getIdentifierTable().getSymbolHandle(runtime_, str, hash));
372
2
  }
373
236
  stringIDMap_[stringID] = RootSymbolID(id);
374
236
  return id;
375
236
}
hermes::vm::SymbolID hermes::vm::RuntimeModule::mapStringMayAllocate<char>(llvh::ArrayRef<char>, unsigned int, unsigned int)
Line
Count
Source
355
13.5k
    uint32_t hash) {
356
  // Create a SymbolID for a given string. In general a SymbolID holds onto an
357
  // intern'd StringPrimitive. As an optimization, if this RuntimeModule is
358
  // persistent, then it will not be deallocated before the Runtime, and we can
359
  // have the SymbolID hold a raw pointer into the storage and produce the
360
  // StringPrimitive when it is first required.
361
13.5k
  SymbolID id;
362
13.5k
  if (flags_.persistent) {
363
    // Registering a lazy identifier does not allocate, so we do not need a
364
    // GC scope.
365
13.4k
    id = runtime_.getIdentifierTable().registerLazyIdentifier(str, hash);
366
13.4k
  } else {
367
    // Accessing a symbol non-lazily may allocate in the GC heap, so add a scope
368
    // marker.
369
122
    GCScopeMarkerRAII scopeMarker{runtime_};
370
122
    id = *runtime_.ignoreAllocationFailure(
371
122
        runtime_.getIdentifierTable().getSymbolHandle(runtime_, str, hash));
372
122
  }
373
13.5k
  stringIDMap_[stringID] = RootSymbolID(id);
374
13.5k
  return id;
375
13.5k
}
376
377
582
void RuntimeModule::markRoots(RootAcceptor &acceptor, bool markLongLived) {
378
582
  for (auto &it : templateMap_) {
379
128
    acceptor.acceptPtr(it.second);
380
128
  }
381
382
582
  if (markLongLived) {
383
19.8k
    for (auto symbol : stringIDMap_) {
384
19.8k
      if (symbol.isValid()) {
385
13.0k
        acceptor.accept(symbol);
386
13.0k
      }
387
19.8k
    }
388
390
  }
389
582
}
390
391
390
void RuntimeModule::markLongLivedWeakRoots(WeakRootAcceptor &acceptor) {
392
11.0k
  for (auto &cbPtr : functionMap_) {
393
    // Only mark a CodeBlock is its non-null, and has not been scanned
394
    // previously in this top-level markRoots invocation.
395
11.0k
    if (cbPtr != nullptr && cbPtr->getRuntimeModule() == this) {
396
5.20k
      cbPtr->markCachedHiddenClasses(runtime_, acceptor);
397
5.20k
    }
398
11.0k
  }
399
390
  for (auto &entry : objectLiteralHiddenClasses_) {
400
0
    if (entry.second) {
401
0
      acceptor.acceptWeak(entry.second);
402
0
    }
403
0
  }
404
390
}
405
406
llvh::Optional<Handle<HiddenClass>> RuntimeModule::findCachedLiteralHiddenClass(
407
    Runtime &runtime,
408
    unsigned keyBufferIndex,
409
0
    unsigned numLiterals) const {
410
0
  if (canGenerateLiteralHiddenClassCacheKey(keyBufferIndex, numLiterals)) {
411
0
    const auto cachedHiddenClassIter = objectLiteralHiddenClasses_.find(
412
0
        getLiteralHiddenClassCacheHashKey(keyBufferIndex, numLiterals));
413
0
    if (cachedHiddenClassIter != objectLiteralHiddenClasses_.end()) {
414
0
      if (HiddenClass *const cachedHiddenClass =
415
0
              cachedHiddenClassIter->second.get(runtime, runtime.getHeap())) {
416
0
        return runtime_.makeHandle(cachedHiddenClass);
417
0
      }
418
0
    }
419
0
  }
420
0
  return llvh::None;
421
0
}
422
423
void RuntimeModule::tryCacheLiteralHiddenClass(
424
    Runtime &runtime,
425
    unsigned keyBufferIndex,
426
0
    HiddenClass *clazz) {
427
0
  auto numLiterals = clazz->getNumProperties();
428
0
  if (canGenerateLiteralHiddenClassCacheKey(keyBufferIndex, numLiterals)) {
429
0
    assert(
430
0
        !findCachedLiteralHiddenClass(runtime, keyBufferIndex, numLiterals)
431
0
             .hasValue() &&
432
0
        "Why are we caching an item already cached?");
433
0
    objectLiteralHiddenClasses_[getLiteralHiddenClassCacheHashKey(
434
0
                                    keyBufferIndex, numLiterals)]
435
0
        .set(runtime, clazz);
436
0
  }
437
0
}
438
439
0
size_t RuntimeModule::additionalMemorySize() const {
440
0
  return stringIDMap_.capacity() * sizeof(SymbolID) +
441
0
      objectLiteralHiddenClasses_.getMemorySize() +
442
0
      templateMap_.getMemorySize();
443
0
}
444
445
#ifdef HERMES_MEMORY_INSTRUMENTATION
446
0
void RuntimeModule::snapshotAddNodes(GC &gc, HeapSnapshot &snap) const {
447
  // Create a native node for each CodeBlock owned by this module.
448
0
  for (const CodeBlock *cb : functionMap_) {
449
    // Skip the null code blocks, they are lazily inserted the first time
450
    // they are used.
451
0
    if (cb && cb->getRuntimeModule() == this) {
452
      // Only add a CodeBlock if this runtime module is the owner.
453
0
      snap.beginNode();
454
0
      snap.endNode(
455
0
          HeapSnapshot::NodeType::Native,
456
0
          "CodeBlock",
457
0
          gc.getNativeID(cb),
458
0
          sizeof(CodeBlock) + cb->additionalMemorySize(),
459
0
          0);
460
0
    }
461
0
  }
462
463
  // Create a node for functionMap_.
464
0
  snap.beginNode();
465
  // Create an edge to each CodeBlock owned by this module.
466
0
  for (int i = 0, e = functionMap_.size(); i < e; i++) {
467
0
    const CodeBlock *cb = functionMap_[i];
468
    // Skip the null code blocks, they are lazily inserted the first time
469
    // they are used.
470
0
    if (cb && cb->getRuntimeModule() == this) {
471
      // Only add a CodeBlock if this runtime module is the owner.
472
0
      snap.addIndexedEdge(
473
0
          HeapSnapshot::EdgeType::Element, i, gc.getNativeID(cb));
474
0
    }
475
0
  }
476
0
  snap.endNode(
477
0
      HeapSnapshot::NodeType::Native,
478
0
      "std::vector<CodeBlock *>",
479
0
      gc.getNativeID(&functionMap_),
480
0
      functionMap_.capacity() * sizeof(CodeBlock *),
481
0
      0);
482
0
}
483
484
0
void RuntimeModule::snapshotAddEdges(GC &gc, HeapSnapshot &snap) const {
485
0
  snap.addNamedEdge(
486
0
      HeapSnapshot::EdgeType::Internal,
487
0
      "functionMap",
488
0
      gc.getNativeID(&functionMap_));
489
0
}
490
#endif // HERMES_MEMORY_INSTRUMENTATION
491
492
namespace detail {
493
494
0
StringID mapStringMayAllocate(RuntimeModule &module, const char *str) {
495
0
  module.stringIDMap_.push_back({});
496
0
  module.mapStringMayAllocate(
497
0
      createASCIIRef(str), module.stringIDMap_.size() - 1);
498
0
  return module.stringIDMap_.size() - 1;
499
0
}
500
501
} // namespace detail
502
503
} // namespace vm
504
505
} // namespace hermes