Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/include/hermes/VM/GCBase.h
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
#ifndef HERMES_VM_GCBASE_H
9
#define HERMES_VM_GCBASE_H
10
11
#include "hermes/ADT/ManagedChunkedList.h"
12
#include "hermes/Inst/Inst.h"
13
#include "hermes/Platform/Logging.h"
14
#include "hermes/Public/CrashManager.h"
15
#include "hermes/Public/GCConfig.h"
16
#include "hermes/Public/GCTripwireContext.h"
17
#include "hermes/Support/CheckedMalloc.h"
18
#include "hermes/Support/OSCompat.h"
19
#include "hermes/Support/StatsAccumulator.h"
20
#include "hermes/VM/AllocOptions.h"
21
#include "hermes/VM/BuildMetadata.h"
22
#include "hermes/VM/CellKind.h"
23
#include "hermes/VM/CompressedPointer.h"
24
#include "hermes/VM/GCDecl.h"
25
#include "hermes/VM/GCExecTrace.h"
26
#include "hermes/VM/GCPointer.h"
27
#include "hermes/VM/HeapAlign.h"
28
#include "hermes/VM/HeapSnapshot.h"
29
#include "hermes/VM/HermesValue.h"
30
#include "hermes/VM/SlotAcceptor.h"
31
#include "hermes/VM/SlotVisitor.h"
32
#include "hermes/VM/SmallHermesValue.h"
33
#include "hermes/VM/StackTracesTree-NoRuntime.h"
34
#include "hermes/VM/StorageProvider.h"
35
#include "hermes/VM/StringRefUtils.h"
36
#include "hermes/VM/VTable.h"
37
#include "hermes/VM/WeakRefSlot.h"
38
39
#include "llvh/ADT/ArrayRef.h"
40
#include "llvh/ADT/BitVector.h"
41
#include "llvh/ADT/DenseMap.h"
42
#include "llvh/Support/ErrorHandling.h"
43
44
#include <cassert>
45
#include <chrono>
46
#include <cstdint>
47
#include <cstdlib>
48
#include <cstring>
49
#include <list>
50
#include <random>
51
#include <system_error>
52
#include <vector>
53
#pragma GCC diagnostic push
54
55
namespace hermes {
56
namespace vm {
57
58
/// Forward declarations;
59
class GCCell;
60
class JSObject;
61
class JSWeakMapImplBase;
62
63
#ifdef HERMESVM_GC_RUNTIME
64
#define RUNTIME_GC_KINDS GC_KIND(HadesGC)
65
#endif
66
67
/// Used by XorPtr to separate encryption keys between uses.
68
enum XorPtrKeyID {
69
  ArrayBufferData,
70
  JSFunctionCodeBlock,
71
  DummyObjectFinalizerCallback,
72
  _NumKeys
73
};
74
75
// A specific GC class extend GCBase, and override its virtual functions.
76
// In addition, it must implement the following methods:
77
78
/// Allocate a new cell of type \p T and size \p size. Instantiate an object of
79
/// type \p T in the newly allocated cell, using \p args as the arguments to its
80
/// constructor. If necessary perform a GC cycle, which may potentially move
81
/// allocated objects. \p fixedSize should indicate whether the allocation is
82
/// for a fixed-size, small object; some GCs may allow optimizations on this
83
/// basis. \p hasFinalizer must be \p HasFinalizer::Yes if cells of the given
84
/// type require a finalizer to be called.
85
/// \pre size must be heap-aligned.
86
///
87
///   template <
88
///     typename T,
89
///     bool fixedSize = true,
90
///     HasFinalizer hasFinalizer = HasFinalizer::No,
91
///     LongLived longLived = LongLived::No,
92
///     class... Args>
93
/// inline T *makeA(uint32_t size, Args &&... args);
94
///
95
/// In some GCs, objects can have associated memory allocated outside the heap,
96
/// and this memory can influence GC initiation and heap sizing heuristics.
97
/// This method tests whether an external memory allocation is too large (e.g.,
98
/// larger than the max size of the heap):
99
///
100
///   bool canAllocExternalMemory(uint32_t size);
101
///
102
/// These APIs inform the GC of this external memory.
103
///
104
///   void creditExternalMemory(GCCell *alloc, uint32_t size);
105
///   void debitExternalMemory(GCCell *alloc, uint32_t size);
106
///
107
/// Force a garbage collection cycle. The provided cause will be used in
108
/// logging.
109
///   void collect(std::string cause);
110
///
111
/// The maximum size of any one allocation allowable by the GC in any state.
112
///   static constexpr uint32_t maxAllocationSizeImpl();
113
///
114
/// Mark a pointer to a GCCell.
115
///   template <class T> void mark(T *&ptr);
116
///
117
/// Returns true if \p p points into the heap.
118
///   bool contains(const void *p) const;
119
///
120
/// Return the lower bound of the heap's virtual address range (inclusive).
121
///   char *lowLim() const;
122
///
123
/// Return the upper bound of the heap's virtual address range (exclusive).
124
///   char *hiLim() const;
125
///
126
/// Iterate over all objects in the heap.
127
///   void forAllObjs(const std::function<void(GCCell *)> &callback);
128
///
129
/// In the "mark" functions below, Name is one of const char*, int,
130
/// unsigned, or const StringPrimitive*:
131
///
132
/// Mark a HermesValue which may or may not be a pointer.
133
///   void mark(HermesValue &hv, Name name);
134
///   void mark(HermesValue &hv);
135
///
136
/// Mark a T* location. This location must be outside the heap.
137
///   void mark(T *&ptr, Name name);
138
///   void mark(T *&ptr);
139
///
140
/// Mark a GCPointer<T>, which must be within the heap.
141
///   void mark(GCPointer<T> &ptr, Name name);
142
///
143
/// Return true if the GC is active and is calling into the VM.
144
/// If true, some objects in the heap may have an invalid VTable pointer.
145
///   bool calledByGC() const;
146
///
147
/// Various forms of write barriers: these can have empty implementations
148
/// for GCs that don't require them:
149
///
150
///   The given value is being written at the given loc (required to
151
///   be in the heap).  If value is a pointer, execute a write barrier.
152
///     void writeBarrier(GCHermesValue *loc, HermesValue value);
153
///     void writeBarrier(
154
///         const GCSmallHermesValue *loc,
155
///         SmallHermesValue value);
156
///
157
///   The given pointer value is being written at the given loc (required to
158
///   be in the heap).  The value is may be null.  Execute a write barrier.
159
///     void writeBarrier(const GCPointerBase *loc, const GCCell *value);
160
///
161
///   The given value/pointer is being written at a previously uninitialized loc
162
///   (required to be in the heap).
163
///     void constructorWriteBarrier(
164
///         const GCHermesValue *loc,
165
///         HermesValue value);
166
///     void constructorWriteBarrier(
167
///         const GCSmallHermesValue *loc,
168
///         SmallHermesValue value);
169
///     void constructorWriteBarrier(
170
///         const GCPointerBase *loc,
171
///         const GCCell *value);
172
///
173
///   A weak ref is about to be read. Executes a read barrier so the GC can
174
///   take action such as extending the lifetime of the reference.
175
///     void weakRefReadBarrier(GCCell *value);
176
///   A mapped value in WeakMap/WeakSet is about to be read. Executes a read
177
///   barrier so the GC can take action such as extending the lifetime of the
178
///   mapped value.
179
///     void weakRefReadBarrier(HermesValue value);
180
///
181
///   We copied HermesValues into the given region.  Note that \p numHVs is
182
///   the number of HermesValues in the the range, not the char length.
183
///   Do any necessary barriers.
184
///     void writeBarrierRange(GCHermesValue* start, uint32_t numHVs);
185
///     void writeBarrierRange(
186
///         const GCSmallHermesValue *start, uint32_t
187
///         numHVs);
188
///     void constructorWriteBarrierRange(
189
///         const GCHermesValue *start,
190
///         uint32_t numHVs);
191
///     void constructorWriteBarrierRange(
192
///         const GCSmallHermesValue *start,
193
///         uint32_t numHVs);
194
///
195
///   The given loc or region is about to be overwritten, but the new value is
196
///   not important. Perform any necessary barriers.
197
///     void snapshotWriteBarrier(const GCHermesValue *loc);
198
///     void snapshotWriteBarrier(const GCSmallHermesValue *loc);
199
///     void snapshotWriteBarrier(const GCPointerBase *loc);
200
///     void snapshotWriteBarrier(const GCSymboldID *symbol);
201
///     void snapshotWriteBarrierRange(
202
///         const GCHermesValue *start,
203
///         uint32_t numHVs);
204
///     void snapshotWriteBarrierRange(
205
///         const GCSmallHermesValue *start,
206
///         uint32_t numHVs);
207
///
208
///   In debug builds: is a write barrier necessary for a write of the given
209
///   GC pointer \p value to the given \p loc?
210
///      bool needsWriteBarrier(void *loc, void *value);
211
///
212
/// This is intended to be called only from within object or root mark
213
/// functions and indicates whether the \c mark() operation, called within
214
/// the current GC phase, is the first such call that guarantees that the
215
/// location passed to mark will contain the final, correct, pointer value
216
/// after the mark call.
217
///   bool isUpdatingPointers() const;
218
///
219
/// It must also have the inner type:
220
///   class Size;
221
/// Which provides at least these functions publicly:
222
///   Constructor from either a GCConfig or the min and max heap size.
223
///     explicit Size(const GCConfig &conf);
224
///     Size(gcheapsize_t min, gcheapsize_t max);
225
///   Return the minimum amount of bytes holdable by this heap.
226
///     gcheapsize_t min() const;
227
///   Return the maximum amount of bytes holdable by this heap.
228
///     gcheapsize_t max() const;
229
///   Return the total amount of bytes of storage this GC will require.
230
///   This will be a multiple of AlignedStorage::size().
231
///     gcheapsize_t storageFootprint() const;
232
///
233
class GCBase {
234
 public:
235
  static const char kNaturalCauseForAnalytics[];
236
  static const char kHandleSanCauseForAnalytics[];
237
238
  /// An interface enabling the garbage collector to mark roots and free
239
  /// symbols.
240
  struct GCCallbacks {
241
    /// Virtual destructor to avoid warnings.
242
    virtual ~GCCallbacks() = 0;
243
244
    /// Callback that will be invoked by the GC to mark all roots in the
245
    /// beginning of every GC by calling "gc->mark()".
246
    /// The \p markLongLived argument indicates whether root data structures
247
    /// that contain only references to long-lived objects (allocated
248
    /// via allocLongLived) are required to be scanned.  A generational
249
    /// collector, for example, might take advantage of this.
250
    virtual void markRoots(
251
        RootAndSlotAcceptorWithNames &acceptor,
252
        bool markLongLived = true) = 0;
253
254
    /// Callback that will be invoked by the GC to mark all weak roots in the
255
    /// beginning of every GC.
256
    virtual void markWeakRoots(
257
        WeakRootAcceptor &weakAcceptor,
258
        bool markLongLived = true) = 0;
259
260
    /// Callback that might be invoked by the GC before it completes marking.
261
    /// Not all GCs will call this. It should be used to mark any roots that
262
    /// might change without executing proper read and write barriers on the GC.
263
    /// An example would be something that skipped weak ref read barriers in a
264
    /// signal handler. If they aren't marked, they would be collected as
265
    /// garbage.
266
    /// While it is possible to implement this just as forwarding to
267
    /// \c markRoots, to be faster it should try to mark only things that would
268
    /// not have been properly doing barriers.
269
    virtual void markRootsForCompleteMarking(
270
        RootAndSlotAcceptorWithNames &acceptor) = 0;
271
272
    /// \return one higher than the largest symbol in the identifier table. This
273
    /// enables the GC to size its internal structures for symbol marking.
274
    /// Optionally invoked at the beginning of a garbage collection.
275
    virtual unsigned getSymbolsEnd() const = 0;
276
277
    /// If any symbols are marked by the IdentifierTable, clear that marking.
278
    /// Optionally invoked at the beginning of some collections.
279
    virtual void unmarkSymbols() = 0;
280
281
    /// Free all symbols which are not marked as \c true in \p markedSymbols.
282
    /// Optionally invoked at the end of a garbage collection.
283
    virtual void freeSymbols(const llvh::BitVector &markedSymbols) = 0;
284
285
    /// Prints any statistics maintained in the Runtime about GC to \p
286
    /// os.  At present, this means the breakdown of markRoots time by
287
    /// "phase" within markRoots.
288
    virtual void printRuntimeGCStats(JSONEmitter &json) const = 0;
289
290
    /// \returns the approximate usage of memory external to the GC such as
291
    /// malloc by the roots of the object graph.
292
    virtual size_t mallocSize() const = 0;
293
294
    /// Visits every entry in the identifier table and calls acceptor with
295
    /// the entry as argument. This is intended to be used only for Snapshots,
296
    /// as it is slow. The function passed as acceptor shouldn't perform any
297
    /// heap operations.
298
    virtual void visitIdentifiers(
299
        const std::function<void(SymbolID, const StringPrimitive *)>
300
            &acceptor) = 0;
301
302
    /// Convert the given symbol into its UTF-8 string representation.
303
    /// \post The implementation of this function must not perform any GC
304
    ///   operations, such as allocations, mutating values in the heap, or
305
    ///   making handles.
306
    virtual std::string convertSymbolToUTF8(SymbolID id) = 0;
307
308
    /// Returns the current stack as a string. This function will not cause
309
    /// any allocs in the GC.
310
    virtual std::string getCallStackNoAlloc() = 0;
311
312
    /// This is called with CollectionStart at the start of each GC, and with
313
    /// CollectionEnd at the end.
314
    /// \param extraInfo contains more detailed extra info for specific GC.
315
    virtual void onGCEvent(GCEventKind kind, const std::string &extraInfo) = 0;
316
317
    /// Return the current VM instruction pointer which can be used to derive
318
    /// the current VM stack-trace. It's "slow" because it's virtual.
319
    virtual const inst::Inst *getCurrentIPSlow() const = 0;
320
321
#ifdef HERMES_MEMORY_INSTRUMENTATION
322
    /// Return a \c StackTracesTreeNode representing the current VM stack-trace
323
    /// at this point.
324
    virtual StackTracesTreeNode *getCurrentStackTracesTreeNode(
325
        const inst::Inst *ip) = 0;
326
327
    /// Get a StackTraceTree which can be used to recover stack-traces from \c
328
    /// StackTraceTreeNode() as returned by \c getCurrentStackTracesTreeNode() .
329
    virtual StackTracesTree *getStackTracesTree() = 0;
330
#endif
331
332
#ifdef HERMES_SLOW_DEBUG
333
    /// \return true if the given symbol is a live entry in the identifier
334
    /// table.
335
    /// NOTE: Used by CheckHeapWellFormedAcceptor to make sure a symbol
336
    /// that is discovered live is marked as live.
337
    virtual bool isSymbolLive(SymbolID id) = 0;
338
339
    /// \return An associated heap cell for the symbol if one exists, null
340
    /// otherwise.
341
    virtual const void *getStringForSymbol(SymbolID id) = 0;
342
#endif
343
  };
344
345
  /// Stats for collections. Time unit, where applicable, is seconds.
346
  struct CumulativeHeapStats {
347
    unsigned numCollections{0};
348
349
    /// Summary statistics for GC wall times. For Hades, this should only track
350
    /// time spent on the mutator.
351
    StatsAccumulator<double> gcWallTime;
352
353
    /// Summary statistics for GC CPU times.
354
    StatsAccumulator<double> gcCPUTime;
355
356
    gcheapsize_t finalHeapSize{0};
357
358
    /// Bytes allocated just before a collection.
359
    StatsAccumulator<gcheapsize_t, uint64_t> usedBefore;
360
361
    /// Bytes alive after a collection.
362
    StatsAccumulator<gcheapsize_t, uint64_t> usedAfter;
363
  };
364
365
  struct HeapInfo {
366
    /// Number of garbage collections (of any kind) since creation.
367
    unsigned numCollections{0};
368
    /// Number of compaction since creation (zero if non-generational GC).
369
    unsigned numCompactions{0};
370
    /// Total (cumulative) bytes allocated within the JS heap since creation.
371
    uint64_t totalAllocatedBytes{0};
372
    /// Number of currently allocated bytes within the JS heap. Some may be
373
    /// in unreachable objects (unless a full collection just occurred).
374
    uint64_t allocatedBytes{0};
375
    /// Current capacity of the JS heap, in bytes.
376
    uint64_t heapSize{0};
377
    /// Estimate of amount of current malloc space used by the runtime and any
378
    /// auxiliary allocations owned by heap objects. (Calculated by querying
379
    /// each finalizable object to report its malloc usage.)
380
    unsigned mallocSizeEstimate{0};
381
    /// The total amount of Virtual Address space (VA) that the GC is using.
382
    uint64_t va{0};
383
    /// Number of bytes retained by objects as external memory on the C++ heap.
384
    /// This is typically associated with allocations that are modified
385
    /// infrequently, and can therefore be stored in a counter in the GC, making
386
    /// them cheaper to query. This is a subset of mallocSizeEstimate.
387
    uint64_t externalBytes{0};
388
    /// Cumulative number of mark stack overflows in full collections
389
    /// (zero if non-generational GC).
390
    unsigned numMarkStackOverflows{0};
391
    /// Stats for general collection (including both YG and OG).
392
    CumulativeHeapStats generalStats;
393
    /// Stats for full collections (zeroes if non-generational GC).
394
    CumulativeHeapStats fullStats;
395
    /// Stats for collections in the young generation (zeroes if
396
    /// non-generational GC).
397
    CumulativeHeapStats youngGenStats;
398
  };
399
400
#ifndef NDEBUG
401
  struct DebugHeapInfo {
402
    /// Number of currently allocated objects present in the heap. Some may be
403
    /// unreachable.
404
    unsigned numAllocatedObjects{0};
405
    /// Number of reachable objects in the last collection.
406
    unsigned numReachableObjects{0};
407
    /// Number of collected objects in the last collection.
408
    unsigned numCollectedObjects{0};
409
    /// Number of finalized objects in the last collection.
410
    unsigned numFinalizedObjects{0};
411
    /// Number of marked symbols.
412
    unsigned numMarkedSymbols{0};
413
    /// Number of hidden classes alive after the last collection.
414
    unsigned numHiddenClasses{0};
415
    /// Number of "leaf" hidden classes alive after the last collection.
416
    unsigned numLeafHiddenClasses{0};
417
418
    // Assert any invariants that should hold among the fields of the
419
    // DebugHeapInfo.
420
    void assertInvariants() const;
421
  };
422
#endif
423
424
#ifdef HERMES_MEMORY_INSTRUMENTATION
425
  /// When enabled, every allocation gets an attached stack-trace and an
426
  /// object ID. When disabled old allocations continue to be tracked but
427
  /// no new allocations get a stack-trace.
428
  struct AllocationLocationTracker final {
429
    explicit AllocationLocationTracker(GCBase *gc);
430
431
    /// Returns true if tracking is enabled for new allocations.
432
    bool isEnabled() const;
433
    /// Must be called by GC implementations whenever a new allocation is made.
434
    void newAlloc(const GCCell *ptr, uint32_t sz);
435
436
    /// If an object's size changes, update the entry here.
437
    void updateSize(const GCCell *ptr, uint32_t oldSize, uint32_t newSize);
438
439
    /// Must be called by GC implementations whenever an allocation is freed.
440
    void freeAlloc(const GCCell *ptr, uint32_t sz);
441
    /// Returns data needed to reconstruct the JS stack used to create the
442
    /// specified allocation.
443
    StackTracesTreeNode *getStackTracesTreeNodeForAlloc(
444
        HeapSnapshot::NodeID id) const;
445
446
    /// A Fragment is a time bound for when objects are allocated. Any
447
    /// allocations that occur before the lastSeenObjectID_ are in this
448
    /// fragment. Allocations increment the numObjects_ and numBytes_. Free'd
449
    /// cells from this fragment decrement numObjects_ and numBytes_.
450
    struct Fragment {
451
      HeapSnapshot::NodeID lastSeenObjectID_;
452
      std::chrono::microseconds timestamp_;
453
      /// Number of objects still alive in this fragment. Incremented when
454
      /// objects are created, decremented when objects are destroyed.
455
      uint64_t numObjects_;
456
      /// Total size of objects still alive in this fragment.
457
      uint64_t numBytes_;
458
      /// If true, one of numObjects or numBytes changed since the last flush.
459
      bool touchedSinceLastFlush_;
460
    };
461
462
    /// This must match the definition in jsi::Instrumentation to avoid
463
    /// unnecessary copying.
464
    using HeapStatsUpdate = std::tuple<uint64_t, uint64_t, uint64_t>;
465
466
    /// Enable location tracking.
467
    void enable(
468
        std::function<void(
469
            uint64_t,
470
            std::chrono::microseconds,
471
            std::vector<HeapStatsUpdate>)> callback);
472
473
    /// Disable location tracking - turns \c newAlloc() into a no-op. Existing
474
    /// allocations continue to be tracked.
475
    void disable();
476
477
    /// Flush the current fragment and write all flushed fragments to \p snap.
478
    void addSamplesToSnapshot(HeapSnapshot &snap);
479
480
   private:
481
    /// Flush out heap profiler data to the callback after a new kFlushThreshold
482
    /// bytes are allocated.
483
    static constexpr uint64_t kFlushThreshold = 128 * (1 << 10);
484
    /// This mutex protects stackMap_ and fragments_. Specifically does not
485
    /// protect enabled_, because enabled_ should only be changed while the GC
486
    /// isn't running anyway.
487
    Mutex mtx_;
488
    /// Associates allocations at their current location with their stack trace
489
    /// data.
490
    llvh::DenseMap<HeapSnapshot::NodeID, StackTracesTreeNode *> stackMap_;
491
    /// We need access to the GCBase to collect the current stack when nodes are
492
    /// allocated.
493
    GCBase *gc_;
494
    /// Indicates if tracking of new allocations is enabled.
495
    bool enabled_{false};
496
    /// Time when the profiler was started.
497
    std::chrono::steady_clock::time_point startTime_;
498
    /// This should be called periodically whenever the last seen object ID is
499
    /// updated.
500
    std::function<
501
        void(uint64_t, std::chrono::microseconds, std::vector<HeapStatsUpdate>)>
502
        fragmentCallback_;
503
    /// All samples that have been flushed. Only needs the last object ID to be
504
    /// written to the file.
505
    std::vector<Fragment> fragments_;
506
507
    /// Updates the last fragment to have the current last ID and timestamp,
508
    /// then calls fragmentCallback_ with both the new fragment and any changed
509
    /// fragments from freeAlloc.
510
    void flushCallback();
511
512
    /// Find the fragment corresponding to the given id.
513
    /// \return fragments_.back() if none exists (it's the currently active
514
    ///   fragment).
515
    Fragment &findFragmentForID(HeapSnapshot::NodeID id);
516
  };
517
518
  class SamplingAllocationLocationTracker final {
519
   public:
520
113
    explicit inline SamplingAllocationLocationTracker(GCBase *gc) : gc_(gc) {}
521
522
    /// Returns true if tracking is enabled for new allocations.
523
3.26M
    bool isEnabled() const {
524
3.26M
      return !!dist_;
525
3.26M
    }
526
527
    /// Must be called by GC implementations whenever a new allocation is made.
528
    void newAlloc(const GCCell *ptr, uint32_t sz);
529
530
    /// Must be called by GC implementations whenever an allocation is freed.
531
    void freeAlloc(const GCCell *ptr, uint32_t sz);
532
533
    /// If an object's size changes, update the entry here.
534
    void updateSize(const GCCell *ptr, uint32_t oldSize, uint32_t newSize);
535
536
    /// Turn the sampling memory profiler on. About once every
537
    /// \p samplingInterval bytes are allocated, sample the allocation by
538
    /// recording its stack.
539
    /// \param seed If non-negative, use as the seed for the random sampling
540
    ///   mechanism, giving deterministic output.
541
    void enable(size_t samplingInterval, int64_t seed);
542
543
    void disable(llvh::raw_ostream &os);
544
545
   private:
546
    struct Sample final {
547
      size_t size;
548
      StackTracesTreeNode *node;
549
      /// This is the auto-incremented sample ID, not the ID of the object
550
      /// associated with the sample.
551
      uint64_t id;
552
    };
553
554
    /// This mutex protects stackMap_ and samples_. Not needed for enabling and
555
    /// disabling because those only happen while the world is stopped.
556
    Mutex mtx_;
557
558
    GCBase *gc_;
559
560
    /// Subtract from this each allocation. If it would underflow below zero,
561
    /// take a sample.
562
    size_t limit_{0};
563
564
    /// Track all samples that have been taken.
565
    llvh::DenseMap<HeapSnapshot::NodeID, Sample> samples_;
566
567
    /// Use a poisson distribution to decide when to take the next sample.
568
    std::minstd_rand randomEngine_;
569
    std::unique_ptr<std::poisson_distribution<>> dist_;
570
571
    /// An auto-incrementing integer representing a unique ID for a sample.
572
    /// Used for ordering samples.
573
    uint64_t nextSampleID_{1};
574
575
    /// \return How many bytes should be waited until the next sample.
576
    size_t nextSample();
577
  };
578
#endif
579
580
  class IDTracker final {
581
   public:
582
    /// These are IDs that are reserved for special objects.
583
    enum class ReservedObjectID : HeapSnapshot::NodeID {
584
      // The ID for the super root object.
585
      SuperRoot,
586
      // The ID for the (GC roots) object.
587
      GCRoots,
588
#define ROOT_SECTION(name) name,
589
#include "hermes/VM/RootSections.def"
590
      IdentifierTableLookupVector,
591
      IdentifierTableHashTable,
592
      IdentifierTableMarkedSymbols,
593
      JSIHermesValueList,
594
      JSIWeakHermesValueList,
595
      WeakRefSlotStorage,
596
      Undefined,
597
      Null,
598
      True,
599
      False,
600
      Number,
601
      FirstNonReservedID,
602
    };
603
604
    // 0 is guaranteed to never be a valid node ID.
605
    static constexpr HeapSnapshot::NodeID kInvalidNode = 0;
606
607
113
    static constexpr HeapSnapshot::NodeID reserved(ReservedObjectID id) {
608
      // All reserved IDs should be odd numbers to signify that they're JS
609
      // objects and not native objects. This follows v8's output.
610
113
      return static_cast<std::underlying_type<ReservedObjectID>::type>(id) * 2 +
611
113
          1;
612
113
    }
613
614
    /// A comparator for doubles that allows NaN.
615
    struct DoubleComparator {
616
0
      static double getEmptyKey() {
617
        // Use a non-canonical NaN value as an empty value, which should never
618
        // occur naturally.
619
        // NOTE: HermesValue uses NaN tagging internally so we can use that to
620
        // get the encoding.
621
0
        return llvh::BitsToDouble(HermesValue::encodeUndefinedValue().getRaw());
622
0
      }
623
0
      static double getTombstoneKey() {
624
        // Use a non-canonical NaN value as the tombstone, which should never
625
        // occur naturally.
626
        // NOTE: HermesValue uses NaN tagging internally so we can use that to
627
        // get the encoding.
628
0
        return llvh::BitsToDouble(HermesValue::encodeNullValue().getRaw());
629
0
      }
630
0
      static unsigned getHashValue(double val) {
631
0
        uint64_t bits = llvh::DoubleToBits(val);
632
        // Representation of small floating values may have many lower bits as
633
        // 0's. Hash functions that generate 64bits hashes identical to the
634
        // input integer values (e.g., std::hash) may cause a lot of collisions,
635
        // since the hash values are truncated to 32bits. The LLVM hash_value
636
        // function has the nice property that each input bit affects each
637
        // output bit with close probability, so the hash value after truncating
638
        // is still good. On a random number set of uniform distribution (with
639
        // 1/16 being random doubles and the rest integers) in the range of
640
        // [-1000000, 1000000], with size 80M, this performs pretty well. In
641
        // practice, most numbers in the heap would be small integers, so use
642
        // this for now until we see other extreme cases.
643
0
        return llvh::hash_value(bits);
644
0
      }
645
0
      static bool isEqual(double LHS, double RHS) {
646
0
        return llvh::DoubleToBits(LHS) == llvh::DoubleToBits(RHS);
647
0
      }
648
    };
649
650
    explicit IDTracker();
651
652
    /// Return true if Object IDs have been tracked.
653
    bool hasTrackedObjectIDs();
654
655
    /// Return true if Number IDs are being tracked.
656
    bool isTrackingNumberIDs();
657
658
    /// Start assigning unique IDs to numbers passed to \p getNumberID
659
    void startTrackingNumberIDs();
660
661
    /// Stop assigning unique IDs to numbers passed to \p getNumberID
662
    void stopTrackingNumberIDs();
663
664
    /// Get the unique object id of the given object.
665
    /// If one does not yet exist, start tracking it.
666
    HeapSnapshot::NodeID getObjectID(CompressedPointer cell);
667
668
    /// \return true if the cell has an object ID associated with it, false if
669
    ///   there is none.
670
    bool hasObjectID(CompressedPointer cell);
671
672
    /// Same as \c getObjectID, except it asserts if the cell doesn't have an
673
    /// ID.
674
    HeapSnapshot::NodeID getObjectIDMustExist(CompressedPointer cell);
675
676
    /// Get the unique object id of the symbol with the given symbol \p sym. If
677
    /// one does not yet exist, start tracking it.
678
    HeapSnapshot::NodeID getObjectID(SymbolID sym);
679
680
    /// Get the unique object id of the given native memory (non-JS-heap).
681
    /// If one does not yet exist, start tracking it.
682
    HeapSnapshot::NodeID getNativeID(const void *mem);
683
684
    /// Get a list of IDs for native memory attached to the given node.
685
    /// List will be empty if nothing is attached yet. Then push onto the end
686
    /// with nextNativeID().
687
    /// When the NodeID that these are attached to is untracked, so are the
688
    /// attached native NodeIDs.
689
    llvh::SmallVector<HeapSnapshot::NodeID, 1> &getExtraNativeIDs(
690
        HeapSnapshot::NodeID node);
691
692
    /// If \p isTrackingNumberIDs_ is true, then assign a unique ID to a literal
693
    /// number value that occurs in the heap. Can be used to make fake nodes
694
    /// that will display their numeric value. Otherwise if \p
695
    /// isTrackingNumberIDs_ is false, then simply returns the reserved ID
696
    /// representing Number.
697
    HeapSnapshot::NodeID getNumberID(double num);
698
699
    /// Get the object pointer for the given ID. This is the inverse of \c
700
    /// getObjectID.
701
    /// Returns none if there is no object for that ID.
702
    llvh::Optional<CompressedPointer> getObjectForID(HeapSnapshot::NodeID id);
703
704
    /// Tell the tracker that an object has moved locations.
705
    /// This must be called in a safe order, if A moves to B, and C moves to A,
706
    /// the first move must be recorded before the second.
707
    void moveObject(
708
        CompressedPointer oldLocation,
709
        CompressedPointer newLocation);
710
711
    /// Remove the object from being tracked. This should be done to keep the
712
    /// tracking working set small.
713
    void untrackObject(CompressedPointer cell);
714
715
    /// Remove the symbol from being tracked. This needs to be done to allow
716
    /// symbols to be re-used.
717
    void untrackSymbol(uint32_t symIdx);
718
719
    /// Remove the native memory from being tracked. This should be done to keep
720
    /// the tracking working set small. It is also required to be done when
721
    /// malloc'ed memory is freed, since addresses can be re-used by future
722
    /// allocations.
723
    void untrackNative(const void *mem);
724
725
    /// \return True if this is tracking any native IDs, false if there are no
726
    ///   native IDs being tracked.
727
    bool hasNativeIDs();
728
729
    /// Get the current last ID. All other existing IDs are less than or equal
730
    /// to this one.
731
    HeapSnapshot::NodeID lastID() const;
732
733
    /// Get the next unique native ID for a chunk of native memory.
734
    /// NOTE: public to get assigned native ids without needing to reserve in
735
    /// advance.
736
    HeapSnapshot::NodeID nextNativeID();
737
738
   private:
739
    /// Get the next unique object ID for a newly created object.
740
    HeapSnapshot::NodeID nextObjectID();
741
    /// Get the next unique number ID for a number.
742
    HeapSnapshot::NodeID nextNumberID();
743
744
    /// JS heap nodes are represented by odd-numbered IDs, while native nodes
745
    /// are represented with even-numbered IDs. This requirement is enforced by
746
    /// the Chrome snapshot viewer.
747
    static constexpr HeapSnapshot::NodeID kIDStep = 2;
748
749
    /// This mutex protects objectIDMap_, symbolIDMap_, and numberIDMap_.
750
    /// Specifically does not protect lastID_, since there's only one allocator
751
    /// at a time, and only new allocations affect lastID_.
752
    Mutex mtx_;
753
754
    /// The last ID assigned to a non-native object. Object IDs are not
755
    /// recycled so that snapshots don't confuse two objects with each other.
756
    /// NOTE: Reserved guarantees that this is an odd number.
757
    HeapSnapshot::NodeID lastID_{
758
        reserved(ReservedObjectID::FirstNonReservedID)};
759
760
    /// Map of object pointers to IDs. Only populated once the first heap
761
    /// snapshot is requested, or the first time the memory profiler is turned
762
    /// on, or if JSI tracing is in effect.
763
    /// This map's size is O(number of cells in the heap), which can grow quite
764
    /// large. Using compressed pointers keeps the size small.
765
    llvh::DenseMap<CompressedPointer::RawType, HeapSnapshot::NodeID>
766
        objectIDMap_;
767
768
    /// The inverse of \c objectIDMap_. Only used for debugging views on heap
769
    /// snapshots. To avoid wasting memory in the case where the debugger hasn't
770
    /// requested any, it is populated lazily as each entry is requested. We
771
    /// expect the vast majority of objects aren't inspected in the snapshot.
772
    llvh::DenseMap<HeapSnapshot::NodeID, CompressedPointer::RawType>
773
        idObjectMap_;
774
775
    /// Map of native pointers to IDs. Populated according to
776
    /// the same rules as the objectIDMap_.
777
    llvh::DenseMap<const void *, HeapSnapshot::NodeID> nativeIDMap_;
778
779
    /// Map from a JS heap object ID to additional lazily created IDs for
780
    /// objects. Most useful for native IDs that are attached to a heap object
781
    /// but don't have a stable pointer to use (such as std::vector and
782
    /// llvh::DenseMap).
783
    llvh::DenseMap<
784
        HeapSnapshot::NodeID,
785
        llvh::SmallVector<HeapSnapshot::NodeID, 1>>
786
        extraNativeIDs_;
787
788
    /// Map from symbol indices to unique IDs.  Populated according to
789
    /// the same rules as the objectIDMap_.
790
    llvh::DenseMap<uint32_t, HeapSnapshot::NodeID> symbolIDMap_;
791
792
    /// Map of numeric values to IDs. Used to give numbers in the heap a unique
793
    /// node.
794
    llvh::DenseMap<double, HeapSnapshot::NodeID, DoubleComparator> numberIDMap_;
795
796
    /// Whether to assign an unique ID to the number given to \p getNumberID
797
    bool isTrackingNumberIDs_ = true;
798
  };
799
800
  enum class HeapKind { HadesGC, MallocGC };
801
802
  GCBase(
803
      GCCallbacks &gcCallbacks,
804
      PointerBase &pointerBase,
805
      const GCConfig &gcConfig,
806
      std::shared_ptr<CrashManager> crashMgr,
807
      HeapKind kind);
808
809
113
  virtual ~GCBase() {
810
113
    assert(
811
113
        weakMapEntrySlots_.sizeForTests() == 0 &&
812
113
        "weakMapEntrySlots_ must all be freed");
813
113
    assert(weakSlots_.sizeForTests() == 0 && "weakSlots_ must all be freed");
814
113
  }
815
816
  /// Create a fixed size object of type T.
817
  /// \return a pointer to the newly created object in the GC heap.
818
  template <
819
      typename T,
820
      HasFinalizer hasFinalizer = HasFinalizer::No,
821
      LongLived longLived = LongLived::No,
822
      class... Args>
823
  T *makeAFixed(Args &&...args);
824
825
  /// Create a variable size object of type T and size \p size.
826
  /// \return a pointer to the newly created object in the GC heap.
827
  template <
828
      typename T,
829
      HasFinalizer hasFinalizer = HasFinalizer::No,
830
      LongLived longLived = LongLived::No,
831
      class... Args>
832
  T *makeAVariable(uint32_t size, Args &&...args);
833
834
  template <
835
      typename T,
836
      bool fixedSize = true,
837
      HasFinalizer hasFinalizer = HasFinalizer::No,
838
      LongLived longLived = LongLived::No,
839
      class... Args>
840
  T *makeA(uint32_t size, Args &&...args);
841
842
  /// Name to identify this heap in logs.
843
54
  const std::string &getName() const {
844
54
    return name_;
845
54
  }
846
847
  /// \return the base of pointers in the heap.
848
  /// NOTE: This normally should not be needed, Runtime provides it.
849
  /// However in some scenarios there is only a GC available, not a
850
  /// Runtime. In those cases use this function.
851
6.96M
  PointerBase &getPointerBase() const {
852
6.96M
    return pointerBase_;
853
6.96M
  }
854
855
1.24M
  GCCallbacks &getCallbacks() const {
856
1.24M
    return gcCallbacks_;
857
1.24M
  }
858
859
  /// Forwards to the GC callback \p convertSymbolToUTF8, see documentation
860
  /// for that function.
861
0
  std::string convertSymbolToUTF8(SymbolID id) {
862
0
    return gcCallbacks_.convertSymbolToUTF8(id);
863
0
  }
864
865
  /// Called by the Runtime to inform the GC that it is about to execute JS for
866
  /// the first time.
867
  void runtimeWillExecute();
868
869
  /// Inform the GC that TTI has been reached.  (In case, for example,
870
  /// behavior should change at that point.  Default behavior is to do
871
  /// nothing.)
872
0
  virtual void ttiReached() {}
873
874
  /// Do anything necessary to record the current number of allocated
875
  /// objects in numAllocatedObjects_.  Default is to do nothing.
876
0
  virtual void recordNumAllocatedObjects() {}
877
878
  /// Print any and all collected statistics to the give output stream, \p os.
879
  void printAllCollectedStats(llvh::raw_ostream &os);
880
881
  /// Total number of collections of any kind.
882
0
  unsigned getNumGCs() const {
883
0
    return cumStats_.numCollections;
884
0
  }
885
886
  /// Total wall time in seconds of all pauses due to collections so far.
887
0
  double getGCTime() const {
888
0
    return cumStats_.gcWallTime.sum();
889
0
  }
890
891
  /// Total CPU time in seconds of all pauses due to collections so far.
892
0
  double getGCCPUTime() const {
893
0
    return cumStats_.gcCPUTime.sum();
894
0
  }
895
896
0
  GCCallbacks &getGCCallbacks() const {
897
0
    return gcCallbacks_;
898
0
  }
899
900
  /// Cumulative stats over time so far.
901
0
  virtual size_t getPeakAllocatedBytes() const {
902
0
    return cumStats_.usedBefore.max();
903
0
  }
904
0
  virtual size_t getPeakLiveAfterGC() const {
905
0
    return cumStats_.usedAfter.max();
906
0
  }
907
908
  /// Populate \p info with information about the heap.
909
  virtual void getHeapInfo(HeapInfo &info);
910
  /// Same as \c getHeapInfo, and it adds the amount of malloc memory in use.
911
  virtual void getHeapInfoWithMallocSize(HeapInfo &info);
912
913
  /// Return a reference to the GCExecTrace object, which is used if
914
  /// we're keeping track of information about GCs, for tracing, for example.
915
  const GCExecTrace &getGCExecTrace() const;
916
917
  /// Populate \p info with crash manager information about the heap
918
  virtual void getCrashManagerHeapInfo(CrashManager::HeapInformation &info) = 0;
919
920
#ifndef NDEBUG
921
  /// Populate \p info with more detailed information about the heap that is
922
  /// too expensive to know during production builds.
923
  virtual void getDebugHeapInfo(DebugHeapInfo &info);
924
925
  /// \return Number of weak ref slots currently in use.
926
  /// Inefficient. For testing/debugging.
927
  size_t countUsedWeakRefs() const;
928
929
  /// Return true if \p ptr is currently pointing at valid accessable memory,
930
  /// allocated to an object.
931
  virtual bool validPointer(const void *ptr) const = 0;
932
#endif
933
934
#ifdef HERMESVM_GC_RUNTIME
935
  inline static constexpr uint32_t minAllocationSizeImpl();
936
937
  inline static constexpr uint32_t maxAllocationSizeImpl();
938
#endif
939
940
  inline static constexpr uint32_t minAllocationSize();
941
942
  inline static constexpr uint32_t maxAllocationSize();
943
944
  /// Dump detailed heap contents to the given output stream, \p os.
945
  virtual void dump(llvh::raw_ostream &os, bool verbose = false);
946
947
  /// Run the finalizers for all heap objects.
948
  virtual void finalizeAll() = 0;
949
950
  /// Force a garbage collection cycle. The provided cause will be used in
951
  /// logging.
952
  virtual void collect(std::string cause, bool canEffectiveOOM = false) = 0;
953
954
  /// Iterate over all objects in the heap, and call \p callback on them.
955
  /// \param callback A function to call on each found object.
956
  virtual void forAllObjs(const std::function<void(GCCell *)> &callback) = 0;
957
958
  /// \return true if the pointer lives in the young generation.
959
0
  virtual bool inYoungGen(const void *p) const {
960
0
    return false;
961
0
  }
962
963
  /// Returns whether an external allocation of the given \p size fits
964
  /// within the maximum heap size. (Note that this does not guarantee that the
965
  /// allocation will "succeed" -- the size plus the used() of the heap may
966
  /// still exceed the max heap size. But if it fails, the allocation can never
967
  /// succeed.)
968
0
  virtual bool canAllocExternalMemory(uint32_t size) {
969
0
    return true;
970
0
  }
971
972
  WeakRefSlot *allocWeakSlot(CompressedPointer ptr);
973
974
  /// Allocate a slot to use in WeakMap/WeakSet when inserting a new entry.
975
  /// \param key Pointer to the key object.
976
  /// \param value The mapped value by the key \p key.
977
  /// \param owner Pointer to the owning WeakMap/WeakSet.
978
  WeakMapEntrySlot *allocWeakMapEntrySlot(
979
      JSObject *key,
980
      HermesValue value,
981
      JSWeakMapImplBase *owner);
982
983
#ifndef NDEBUG
984
  /// \name Debug APIs
985
  /// \{
986
0
  virtual bool calledByBackgroundThread() const {
987
0
    return false;
988
0
  }
989
0
  virtual bool calledByGC() const {
990
0
    return true;
991
0
  }
992
  virtual bool dbgContains(const void *ptr) const = 0;
993
0
  virtual void trackReachable(CellKind kind, unsigned sz) {}
994
  virtual bool needsWriteBarrier(void *loc, GCCell *value) = 0;
995
  /// \}
996
#endif
997
998
  /// Do any logging of info about the heap that is useful, then dies with a
999
  /// fatal out-of-memory error.
1000
  LLVM_ATTRIBUTE_NORETURN void oom(std::error_code reason);
1001
1002
#ifdef HERMES_MEMORY_INSTRUMENTATION
1003
  /// Creates a snapshot of the heap and writes it to the given \p fileName.
1004
  /// \return An error code on failure, else an empty error code.
1005
  std::error_code createSnapshotToFile(const std::string &fileName);
1006
1007
  /// An edges counter array for each root section. The counter is uninitialized
1008
  /// if a root section is not visited yet.
1009
  using SavedNumRootEdges = std::array<
1010
      llvh::Optional<HeapSizeType>,
1011
      static_cast<unsigned>(RootSectionAcceptor::Section::NumSections)>;
1012
1013
  /// Creates a snapshot of the heap, which includes information about what
1014
  /// objects exist, their sizes, and what they point to.
1015
  virtual void createSnapshot(
1016
      llvh::raw_ostream &os,
1017
      bool captureNumericValue) = 0;
1018
  void createSnapshot(GC &gc, llvh::raw_ostream &os, bool captureNumericValue);
1019
  /// Actual implementation of writing the snapshot. If \p numRootEdges is
1020
  /// uninitialized, it will be populated with the edge counts for each root
1021
  /// section. Otherwise, it will be used to pad the output with additional
1022
  /// edges if necessary so they match the recorded count.
1023
  void createSnapshotImpl(
1024
      GC &gc,
1025
      HeapSnapshot &snap,
1026
      SavedNumRootEdges &numRootEdges);
1027
1028
  /// Subclasses can override and add more specific native memory usage.
1029
  virtual void snapshotAddGCNativeNodes(HeapSnapshot &snap);
1030
1031
  /// Subclasses can override and add more specific edges.
1032
  virtual void snapshotAddGCNativeEdges(HeapSnapshot &snap);
1033
1034
  /// Turn on the heap profiler, which will track when allocations are made and
1035
  /// the stack trace of when they were created.
1036
  virtual void enableHeapProfiler(
1037
      std::function<void(
1038
          uint64_t,
1039
          std::chrono::microseconds,
1040
          std::vector<GCBase::AllocationLocationTracker::HeapStatsUpdate>)>
1041
          fragmentCallback);
1042
1043
  /// Turn off the heap profiler, which will stop tracking new allocations and
1044
  /// not record any stack traces.
1045
  /// Disabling will not forget any objects that are still alive. This way
1046
  /// re-enabling later will still remember earlier objects.
1047
  virtual void disableHeapProfiler();
1048
1049
  /// Turn the sampling memory profiler on. About once every
1050
  /// \p samplingInterval bytes are allocated, sample the allocation by
1051
  /// recording its stack.
1052
  /// \param seed If non-negative, use as the seed for the random sampling
1053
  ///   mechanism, giving deterministic output.
1054
  virtual void enableSamplingHeapProfiler(
1055
      size_t samplingInterval,
1056
      int64_t seed);
1057
1058
  /// Turn off the sampling heap profiler, which will stop tracking new
1059
  /// allocations and not record any stack traces. Write out the results of the
1060
  /// trace to \p os. After this call, any remembered data about sampled objects
1061
  /// will be gone.
1062
  virtual void disableSamplingHeapProfiler(llvh::raw_ostream &os);
1063
#endif // HERMES_MEMORY_INSTRUMENTATION
1064
1065
  /// Inform the GC about external memory retained by objects.
1066
  virtual void creditExternalMemory(GCCell *alloc, uint32_t size) = 0;
1067
  virtual void debitExternalMemory(GCCell *alloc, uint32_t size) = 0;
1068
1069
#ifdef HERMESVM_GC_RUNTIME
1070
  /// Default implementations for read and write barriers: do nothing.
1071
  void writeBarrier(const GCHermesValue *loc, HermesValue value);
1072
  void writeBarrier(const GCSmallHermesValue *loc, SmallHermesValue value);
1073
  void writeBarrier(const GCPointerBase *loc, const GCCell *value);
1074
  void constructorWriteBarrier(const GCHermesValue *loc, HermesValue value);
1075
  void constructorWriteBarrier(
1076
      const GCSmallHermesValue *loc,
1077
      SmallHermesValue value);
1078
  void constructorWriteBarrier(const GCPointerBase *loc, const GCCell *value);
1079
  void writeBarrierRange(const GCHermesValue *start, uint32_t numHVs);
1080
  void writeBarrierRange(const GCSmallHermesValue *start, uint32_t numHVs);
1081
  void constructorWriteBarrierRange(
1082
      const GCHermesValue *start,
1083
      uint32_t numHVs);
1084
  void constructorWriteBarrierRange(
1085
      const GCSmallHermesValue *start,
1086
      uint32_t numHVs);
1087
  void snapshotWriteBarrier(const GCHermesValue *loc);
1088
  void snapshotWriteBarrier(const GCSmallHermesValue *loc);
1089
  void snapshotWriteBarrier(const GCPointerBase *loc);
1090
  void snapshotWriteBarrier(const GCSymbolID *symbol);
1091
  void snapshotWriteBarrierRange(const GCHermesValue *start, uint32_t numHVs);
1092
  void snapshotWriteBarrierRange(
1093
      const GCSmallHermesValue *start,
1094
      uint32_t numHVs);
1095
  void weakRefReadBarrier(HermesValue value);
1096
  void weakRefReadBarrier(GCCell *value);
1097
#endif
1098
1099
  /// @name Marking APIs
1100
  /// @{
1101
1102
  /// Marks a cell by its metadata.
1103
  /// \p cell The heap object to mark.
1104
  /// \p acceptor The action to perform on each slot in the cell.
1105
  template <typename Acceptor>
1106
  inline void markCell(GCCell *cell, Acceptor &acceptor);
1107
1108
  /// Same as the normal \c markCell, but for cells that don't have a valid
1109
  /// CellKind.
1110
  template <typename Acceptor>
1111
  inline void markCell(GCCell *cell, CellKind kind, Acceptor &acceptor);
1112
1113
  /// Same as the normal \c markCell, but takes a visitor instead.
1114
  template <typename Acceptor>
1115
  inline void
1116
  markCell(SlotVisitor<Acceptor> &visitor, GCCell *cell, CellKind kind);
1117
1118
  /// Marks a cell by its metadata, but only for the slots that point between
1119
  /// [begin, end).
1120
  template <typename Acceptor>
1121
  inline void markCellWithinRange(
1122
      SlotVisitor<Acceptor> &visitor,
1123
      GCCell *cell,
1124
      CellKind kind,
1125
      const char *begin,
1126
      const char *end);
1127
1128
  /// Marks a cell by its metadata, and outputs the names of the slots.
1129
  /// Meant to be used by heap snapshots.
1130
  template <typename Acceptor>
1131
  inline void markCellWithNames(
1132
      SlotVisitorWithNames<Acceptor> &visitor,
1133
      GCCell *cell);
1134
1135
  /// @}
1136
1137
  /// If false, all reachable objects in the heap will have a dereference-able
1138
  /// VTable pointer which describes its type and size. If true, both reachable
1139
  /// objects and unreachable objects may not have dereference-able VTable
1140
  /// pointers, and any reads from JS heap memory may give strange results.
1141
380k
  bool inGC() const {
1142
380k
    return inGC_;
1143
380k
  }
1144
1145
0
  HeapKind getKind() const {
1146
0
    return heapKind_;
1147
0
  }
1148
1149
  /// \return A string representation of the kind of GC.
1150
  virtual std::string getKindAsStr() const = 0;
1151
1152
105
  bool isTrackingIDs() {
1153
105
#ifdef HERMES_MEMORY_INSTRUMENTATION
1154
105
    return getIDTracker().hasTrackedObjectIDs() ||
1155
105
        getAllocationLocationTracker().isEnabled() ||
1156
105
        getSamplingAllocationTracker().isEnabled();
1157
#else
1158
    return getIDTracker().hasTrackedObjectIDs();
1159
#endif
1160
105
  }
1161
1162
8.41k
  IDTracker &getIDTracker() {
1163
8.41k
    return idTracker_;
1164
8.41k
  }
1165
1166
#ifdef HERMES_MEMORY_INSTRUMENTATION
1167
105
  AllocationLocationTracker &getAllocationLocationTracker() {
1168
105
    return allocationLocationTracker_;
1169
105
  }
1170
1171
105
  SamplingAllocationLocationTracker &getSamplingAllocationTracker() {
1172
105
    return samplingAllocationTracker_;
1173
105
  }
1174
#endif
1175
1176
  /// \name Snapshot ID methods
1177
  /// \{
1178
  // This set of methods are all mirrors of IDTracker, except with pointer
1179
  // compression done automatically.
1180
  HeapSnapshot::NodeID getObjectID(const GCCell *cell);
1181
  HeapSnapshot::NodeID getObjectIDMustExist(const GCCell *cell);
1182
  HeapSnapshot::NodeID getObjectID(CompressedPointer cell);
1183
  HeapSnapshot::NodeID getObjectID(SymbolID sym);
1184
  HeapSnapshot::NodeID getNativeID(const void *mem);
1185
  /// \return The ID for the given value. If the value cannot be represented
1186
  ///   with an ID, returns None.
1187
  llvh::Optional<HeapSnapshot::NodeID> getSnapshotID(HermesValue val);
1188
1189
  /// \return The object pointer for the given \p id. This is quite slow.
1190
  ///   If there is no such object this returns nullptr.
1191
  void *getObjectForID(HeapSnapshot::NodeID id);
1192
1193
  /// \return True if the given cell has an ID associated with it.
1194
  bool hasObjectID(const GCCell *cell);
1195
  /// Records that a new allocation has occurred.
1196
  void newAlloc(const GCCell *ptr, uint32_t sz);
1197
  /// Moves an object to a new address and a new size for all trackers.
1198
  void moveObject(
1199
      const GCCell *oldPtr,
1200
      uint32_t oldSize,
1201
      const GCCell *newPtr,
1202
      uint32_t newSize);
1203
  /// Untracks a freed object from all trackers.
1204
  void untrackObject(const GCCell *cell, uint32_t sz);
1205
  /// \}
1206
1207
#ifndef NDEBUG
1208
  /// \return The next debug allocation ID for embedding directly into a GCCell.
1209
  /// NOTE: This is not the same ID as is used for stable lifetime tracking, use
1210
  /// \p getObjectID for that.
1211
  uint64_t nextObjectID();
1212
#endif
1213
1214
// Mangling scheme used by MSVC encode public/private into the name.
1215
// As a result, vanilla "ifdef public" trick leads to link errors.
1216
#if defined(UNIT_TEST) || defined(_MSC_VER)
1217
 public:
1218
#else
1219
 protected:
1220
#endif
1221
1222
  /// dataSize is the live data in bytes, now is the current time point. The
1223
  /// function checks these parameters against the limits set at initialisation.
1224
  /// If the conditions are met, the tripwire is triggered and tripwireCallback_
1225
  /// is called.
1226
  void checkTripwire(size_t dataSize);
1227
1228
  // Visibility here is public for unit_tests and protected otherwise
1229
1230
 protected:
1231
  /// An RAII-style object used to denote regions when a GC cycle is considered
1232
  /// active.
1233
  class GCCycle final {
1234
   public:
1235
    explicit GCCycle(GCBase &gc, std::string extraInfo = "");
1236
    ~GCCycle();
1237
1238
0
    const std::string &extraInfo() {
1239
0
      return extraInfo_;
1240
0
    }
1241
1242
   private:
1243
    GCBase &gc_;
1244
    std::string extraInfo_;
1245
    bool previousInGC_;
1246
  };
1247
1248
  /// Returns the number of bytes allocated allocated since the last GC.
1249
  /// TODO: Implement this for heaps other than GenGC
1250
  /// (at which point this can become an abstract function).
1251
0
  virtual gcheapsize_t bytesAllocatedSinceLastGC() const {
1252
0
    return 0;
1253
0
  }
1254
1255
  /// Convenience method to invoke the mark roots function provided at
1256
  /// initialization, using the context provided then (on this heap).
1257
  /// The \p markLongLived argument indicates whether root data structures
1258
  /// containing only pointers to objects allocated via allocLongLived
1259
  /// are required to be marked.  In this collector, such objects will
1260
  /// be allocated in the old gen, and references to them need not be
1261
  /// marked during young-gen collection.
1262
158
  void markRoots(RootAndSlotAcceptorWithNames &acceptor, bool markLongLived) {
1263
158
    gcCallbacks_.markRoots(acceptor, markLongLived);
1264
158
  }
1265
1266
  /// Convenience method to invoke the mark weak roots function provided at
1267
  /// initialization, using the context provided then (on this heap).
1268
158
  void markWeakRoots(WeakRootAcceptor &acceptor, bool markLongLived) {
1269
158
    gcCallbacks_.markWeakRoots(acceptor, markLongLived);
1270
158
    acceptor.beginRootSection(RootAcceptor::Section::WeakRefSlots);
1271
158
    weakSlots_.forEach(
1272
91.9k
        [&acceptor](WeakRefSlot &slot) { slot.markWeakRoots(acceptor); });
1273
1274
158
    weakMapEntrySlots_.forEach(
1275
158
        [&acceptor](WeakMapEntrySlot &slot) { slot.markWeakRoots(acceptor); });
1276
158
    acceptor.endRootSection();
1277
158
  }
1278
1279
  /// Print the cumulative statistics.
1280
  virtual void printStats(JSONEmitter &json);
1281
1282
  /// Record statistics from a single GC, which are specified in the given
1283
  /// \p event, in the overall cumulative stats struct.
1284
  void recordGCStats(const GCAnalyticsEvent &event, bool onMutator);
1285
1286
  /// Record statistics from a single GC, which are specified in the given
1287
  /// \p event, in the given cumulative stats struct.
1288
  void recordGCStats(
1289
      const GCAnalyticsEvent &event,
1290
      CumulativeHeapStats *stats,
1291
      bool onMutator);
1292
1293
  /// Print detailed stats of the breakdown of the roots and heap in terms of
1294
  /// the number of pointers, symbols, HermesValues, etc.
1295
  void sizeDiagnosticCensus(size_t allocatedBytes);
1296
1297
  /// Do any additional GC-specific logging that is useful before dying with
1298
  /// out-of-memory. Takes a char buffer to avoid dynamic allocations.
1299
  virtual void oomDetail(
1300
      llvh::MutableArrayRef<char> detailBuffer,
1301
      std::error_code reason);
1302
1303
  template <typename T, class... Args>
1304
3.26M
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
3.26M
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
3.26M
    constexpr auto kind = T::getCellKind();
1307
3.26M
    cell->setKindAndSize({kind, size});
1308
3.26M
    return cell;
1309
3.26M
  }
hermes::vm::BigIntPrimitive* hermes::vm::GCBase::constructCell<hermes::vm::BigIntPrimitive, unsigned int&>(void*, unsigned int, unsigned int&)
Line
Count
Source
1304
636k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
636k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
636k
    constexpr auto kind = T::getCellKind();
1307
636k
    cell->setKindAndSize({kind, size});
1308
636k
    return cell;
1309
636k
  }
hermes::vm::BoxedDouble* hermes::vm::GCBase::constructCell<hermes::vm::BoxedDouble, double&>(void*, unsigned int, double&)
Line
Count
Source
1304
2.04k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
2.04k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
2.04k
    constexpr auto kind = T::getCellKind();
1307
2.04k
    cell->setKindAndSize({kind, size});
1308
2.04k
    return cell;
1309
2.04k
  }
hermes::vm::ArrayStorageBase<hermes::vm::HermesValue32>* hermes::vm::GCBase::constructCell<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue32>>(void*, unsigned int)
Line
Count
Source
1304
64.7k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
64.7k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
64.7k
    constexpr auto kind = T::getCellKind();
1307
64.7k
    cell->setKindAndSize({kind, size});
1308
64.7k
    return cell;
1309
64.7k
  }
hermes::vm::Environment* hermes::vm::GCBase::constructCell<hermes::vm::Environment, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Environment>&, unsigned int&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Environment>&, unsigned int&)
Line
Count
Source
1304
599
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
599
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
599
    constexpr auto kind = T::getCellKind();
1307
599
    cell->setKindAndSize({kind, size});
1308
599
    return cell;
1309
599
  }
hermes::vm::NativeConstructor* hermes::vm::GCBase::constructCell<hermes::vm::NativeConstructor, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), hermes::vm::CallResult<hermes::vm::PseudoHandle<hermes::vm::JSObject>, (hermes::vm::detail::CallResultSpecialize)6> (*&)(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, void*), hermes::vm::CellKind&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), hermes::vm::CallResult<hermes::vm::PseudoHandle<hermes::vm::JSObject>, (hermes::vm::detail::CallResultSpecialize)6> (*&)(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, void*), hermes::vm::CellKind&)
Line
Count
Source
1304
4.74k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
4.74k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
4.74k
    constexpr auto kind = T::getCellKind();
1307
4.74k
    cell->setKindAndSize({kind, size});
1308
4.74k
    return cell;
1309
4.74k
  }
Unexecuted instantiation: hermes::vm::NativeConstructor* hermes::vm::GCBase::constructCell<hermes::vm::NativeConstructor, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), hermes::vm::CallResult<hermes::vm::PseudoHandle<hermes::vm::JSObject>, (hermes::vm::detail::CallResultSpecialize)6> (*&)(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, void*), hermes::vm::CellKind&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), hermes::vm::CallResult<hermes::vm::PseudoHandle<hermes::vm::JSObject>, (hermes::vm::detail::CallResultSpecialize)6> (*&)(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, void*), hermes::vm::CellKind&)
hermes::vm::ArrayStorageBase<hermes::vm::HermesValue>* hermes::vm::GCBase::constructCell<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue>>(void*, unsigned int)
Line
Count
Source
1304
113
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
113
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
113
    constexpr auto kind = T::getCellKind();
1307
113
    cell->setKindAndSize({kind, size});
1308
113
    return cell;
1309
113
  }
hermes::vm::BoundFunction* hermes::vm::GCBase::constructCell<hermes::vm::BoundFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Callable>&, hermes::vm::MutableHandle<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue> >&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Callable>&, hermes::vm::MutableHandle<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue> >&)
Line
Count
Source
1304
113
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
113
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
113
    constexpr auto kind = T::getCellKind();
1307
113
    cell->setKindAndSize({kind, size});
1308
113
    return cell;
1309
113
  }
hermes::vm::NativeFunction* hermes::vm::GCBase::constructCell<hermes::vm::NativeFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs)>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs))
Line
Count
Source
1304
49.8k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
49.8k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
49.8k
    constexpr auto kind = T::getCellKind();
1307
49.8k
    cell->setKindAndSize({kind, size});
1308
49.8k
    return cell;
1309
49.8k
  }
Unexecuted instantiation: hermes::vm::NativeFunction* hermes::vm::GCBase::constructCell<hermes::vm::NativeFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs)>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs))
hermes::vm::JSFunction* hermes::vm::GCBase::constructCell<hermes::vm::JSFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&)
Line
Count
Source
1304
4.41k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
4.41k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
4.41k
    constexpr auto kind = T::getCellKind();
1307
4.41k
    cell->setKindAndSize({kind, size});
1308
4.41k
    return cell;
1309
4.41k
  }
Unexecuted instantiation: hermes::vm::JSAsyncFunction* hermes::vm::GCBase::constructCell<hermes::vm::JSAsyncFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&)
Unexecuted instantiation: hermes::vm::JSGeneratorFunction* hermes::vm::GCBase::constructCell<hermes::vm::JSGeneratorFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&)
Unexecuted instantiation: hermes::vm::GeneratorInnerFunction* hermes::vm::GCBase::constructCell<hermes::vm::GeneratorInnerFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&, unsigned int>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&, unsigned int&&)
hermes::vm::Domain* hermes::vm::GCBase::constructCell<hermes::vm::Domain>(void*, unsigned int)
Line
Count
Source
1304
373
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
373
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
373
    constexpr auto kind = T::getCellKind();
1307
373
    cell->setKindAndSize({kind, size});
1308
373
    return cell;
1309
373
  }
Unexecuted instantiation: hermes::vm::RequireContext* hermes::vm::GCBase::constructCell<hermes::vm::RequireContext, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
hermes::vm::HiddenClass* hermes::vm::GCBase::constructCell<hermes::vm::HiddenClass, hermes::vm::Runtime&, hermes::vm::ClassFlags&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::SymbolID&, hermes::vm::PropertyFlags&, unsigned int&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::ClassFlags&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::SymbolID&, hermes::vm::PropertyFlags&, unsigned int&)
Line
Count
Source
1304
66.0k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
66.0k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
66.0k
    constexpr auto kind = T::getCellKind();
1307
66.0k
    cell->setKindAndSize({kind, size});
1308
66.0k
    return cell;
1309
66.0k
  }
hermes::vm::DynamicStringPrimitive<char, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, true>, unsigned long&>(void*, unsigned int, unsigned long&)
Line
Count
Source
1304
149k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
149k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
149k
    constexpr auto kind = T::getCellKind();
1307
149k
    cell->setKindAndSize({kind, size});
1308
149k
    return cell;
1309
149k
  }
hermes::vm::DynamicStringPrimitive<char16_t, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, true>, unsigned long&>(void*, unsigned int, unsigned long&)
Line
Count
Source
1304
152
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
152
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
152
    constexpr auto kind = T::getCellKind();
1307
152
    cell->setKindAndSize({kind, size});
1308
152
    return cell;
1309
152
  }
Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, false>, unsigned long&>(void*, unsigned int, unsigned long&)
hermes::vm::DynamicStringPrimitive<char16_t, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, false>, unsigned long&>(void*, unsigned int, unsigned long&)
Line
Count
Source
1304
1
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
1
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
1
    constexpr auto kind = T::getCellKind();
1307
1
    cell->setKindAndSize({kind, size});
1308
1
    return cell;
1309
1
  }
Unexecuted instantiation: hermes::vm::Arguments* hermes::vm::GCBase::constructCell<hermes::vm::Arguments, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&&, hermes::vm::Handle<hermes::vm::HiddenClass>&)
hermes::vm::JSArray* hermes::vm::GCBase::constructCell<hermes::vm::JSArray, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::GCPointerBase::NoBarriers>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::GCPointerBase::NoBarriers&&)
Line
Count
Source
1304
249k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
249k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
249k
    constexpr auto kind = T::getCellKind();
1307
249k
    cell->setKindAndSize({kind, size});
1308
249k
    return cell;
1309
249k
  }
Unexecuted instantiation: hermes::vm::JSArrayIterator* hermes::vm::GCBase::constructCell<hermes::vm::JSArrayIterator, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::IterationKind&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::IterationKind&)
Unexecuted instantiation: hermes::vm::JSArrayBuffer* hermes::vm::GCBase::constructCell<hermes::vm::JSArrayBuffer, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSDataView* hermes::vm::GCBase::constructCell<hermes::vm::JSDataView, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSDate* hermes::vm::GCBase::constructCell<hermes::vm::JSDate, hermes::vm::Runtime&, double&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, double&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
hermes::vm::JSError* hermes::vm::GCBase::constructCell<hermes::vm::JSError, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, bool&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, bool&)
Line
Count
Source
1304
41
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
41
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
41
    constexpr auto kind = T::getCellKind();
1307
41
    cell->setKindAndSize({kind, size});
1308
41
    return cell;
1309
41
  }
Unexecuted instantiation: hermes::vm::JSGenerator* hermes::vm::GCBase::constructCell<hermes::vm::JSGenerator, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
hermes::vm::JSObject* hermes::vm::GCBase::constructCell<hermes::vm::JSObject, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::GCPointerBase::NoBarriers>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::GCPointerBase::NoBarriers&&)
Line
Count
Source
1304
229k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
229k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
229k
    constexpr auto kind = T::getCellKind();
1307
229k
    cell->setKindAndSize({kind, size});
1308
229k
    return cell;
1309
229k
  }
Unexecuted instantiation: hermes::vm::JSProxy* hermes::vm::GCBase::constructCell<hermes::vm::JSProxy, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
hermes::vm::JSRegExp* hermes::vm::GCBase::constructCell<hermes::vm::JSRegExp, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Line
Count
Source
1304
964
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
964
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
964
    constexpr auto kind = T::getCellKind();
1307
964
    cell->setKindAndSize({kind, size});
1308
964
    return cell;
1309
964
  }
Unexecuted instantiation: hermes::vm::JSMapImpl<(hermes::vm::CellKind)47>* hermes::vm::GCBase::constructCell<hermes::vm::JSMapImpl<(hermes::vm::CellKind)47>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSMapImpl<(hermes::vm::CellKind)48>* hermes::vm::GCBase::constructCell<hermes::vm::JSMapImpl<(hermes::vm::CellKind)48>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSMapIteratorImpl<(hermes::vm::CellKind)50>* hermes::vm::GCBase::constructCell<hermes::vm::JSMapIteratorImpl<(hermes::vm::CellKind)50>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSMapIteratorImpl<(hermes::vm::CellKind)49>* hermes::vm::GCBase::constructCell<hermes::vm::JSMapIteratorImpl<(hermes::vm::CellKind)49>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSWeakMapImpl<(hermes::vm::CellKind)51>* hermes::vm::GCBase::constructCell<hermes::vm::JSWeakMapImpl<(hermes::vm::CellKind)51>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSWeakMapImpl<(hermes::vm::CellKind)52>* hermes::vm::GCBase::constructCell<hermes::vm::JSWeakMapImpl<(hermes::vm::CellKind)52>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSWeakRef* hermes::vm::GCBase::constructCell<hermes::vm::JSWeakRef, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::DecoratedObject* hermes::vm::GCBase::constructCell<hermes::vm::DecoratedObject, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, std::__1::unique_ptr<hermes::vm::DecoratedObject::Decoration, std::__1::default_delete<hermes::vm::DecoratedObject::Decoration> > >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, std::__1::unique_ptr<hermes::vm::DecoratedObject::Decoration, std::__1::default_delete<hermes::vm::DecoratedObject::Decoration> >&&)
Unexecuted instantiation: hermes::vm::FinalizableNativeFunction* hermes::vm::GCBase::constructCell<hermes::vm::FinalizableNativeFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), void (*&)(void*)>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), void (*&)(void*))
hermes::vm::HostObject* hermes::vm::GCBase::constructCell<hermes::vm::HostObject, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, std::__1::unique_ptr<hermes::vm::HostObjectProxy, std::__1::default_delete<hermes::vm::HostObjectProxy> > >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, std::__1::unique_ptr<hermes::vm::HostObjectProxy, std::__1::default_delete<hermes::vm::HostObjectProxy> >&&)
Line
Count
Source
1304
113
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
113
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
113
    constexpr auto kind = T::getCellKind();
1307
113
    cell->setKindAndSize({kind, size});
1308
113
    return cell;
1309
113
  }
Unexecuted instantiation: hermes::vm::NativeState* hermes::vm::GCBase::constructCell<hermes::vm::NativeState, void*&, void (*&)(hermes::vm::HadesGC&, hermes::vm::NativeState*)>(void*, unsigned int, void*&, void (*&)(hermes::vm::HadesGC&, hermes::vm::NativeState*))
hermes::vm::JSString* hermes::vm::GCBase::constructCell<hermes::vm::JSString, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::StringPrimitive>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::StringPrimitive>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&)
Line
Count
Source
1304
124
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
124
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
124
    constexpr auto kind = T::getCellKind();
1307
124
    cell->setKindAndSize({kind, size});
1308
124
    return cell;
1309
124
  }
hermes::vm::JSStringIterator* hermes::vm::GCBase::constructCell<hermes::vm::JSStringIterator, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::Handle<hermes::vm::StringPrimitive>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::Handle<hermes::vm::StringPrimitive>&)
Line
Count
Source
1304
2
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
2
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
2
    constexpr auto kind = T::getCellKind();
1307
2
    cell->setKindAndSize({kind, size});
1308
2
    return cell;
1309
2
  }
Unexecuted instantiation: hermes::vm::JSBigInt* hermes::vm::GCBase::constructCell<hermes::vm::JSBigInt, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::BigIntPrimitive>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::BigIntPrimitive>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&)
hermes::vm::JSNumber* hermes::vm::GCBase::constructCell<hermes::vm::JSNumber, hermes::vm::Runtime&, double&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, double&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&)
Line
Count
Source
1304
124k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
124k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
124k
    constexpr auto kind = T::getCellKind();
1307
124k
    cell->setKindAndSize({kind, size});
1308
124k
    return cell;
1309
124k
  }
hermes::vm::JSBoolean* hermes::vm::GCBase::constructCell<hermes::vm::JSBoolean, hermes::vm::Runtime&, bool&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, bool&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&)
Line
Count
Source
1304
113
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
113
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
113
    constexpr auto kind = T::getCellKind();
1307
113
    cell->setKindAndSize({kind, size});
1308
113
    return cell;
1309
113
  }
Unexecuted instantiation: hermes::vm::JSSymbol* hermes::vm::GCBase::constructCell<hermes::vm::JSSymbol, hermes::vm::Runtime&, hermes::vm::SymbolID&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::SymbolID&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&)
hermes::vm::PropertyAccessor* hermes::vm::GCBase::constructCell<hermes::vm::PropertyAccessor, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Callable>&, hermes::vm::Handle<hermes::vm::Callable>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Callable>&, hermes::vm::Handle<hermes::vm::Callable>&)
Line
Count
Source
1304
5.19k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
5.19k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
5.19k
    constexpr auto kind = T::getCellKind();
1307
5.19k
    cell->setKindAndSize({kind, size});
1308
5.19k
    return cell;
1309
5.19k
  }
hermes::vm::FillerCell* hermes::vm::GCBase::constructCell<hermes::vm::FillerCell>(void*, unsigned int)
Line
Count
Source
1304
10
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
10
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
10
    constexpr auto kind = T::getCellKind();
1307
10
    cell->setKindAndSize({kind, size});
1308
10
    return cell;
1309
10
  }
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::Segment* hermes::vm::GCBase::constructCell<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::Segment>(void*, unsigned int)
Line
Count
Source
1304
611
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
611
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
611
    constexpr auto kind = T::getCellKind();
1307
611
    cell->setKindAndSize({kind, size});
1308
611
    return cell;
1309
611
  }
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>* hermes::vm::GCBase::constructCell<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>>(void*, unsigned int)
Line
Count
Source
1304
77
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
77
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
77
    constexpr auto kind = T::getCellKind();
1307
77
    cell->setKindAndSize({kind, size});
1308
77
    return cell;
1309
77
  }
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::Segment* hermes::vm::GCBase::constructCell<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::Segment>(void*, unsigned int)
Line
Count
Source
1304
1.16k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
1.16k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
1.16k
    constexpr auto kind = T::getCellKind();
1307
1.16k
    cell->setKindAndSize({kind, size});
1308
1.16k
    return cell;
1309
1.16k
  }
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>* hermes::vm::GCBase::constructCell<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>>(void*, unsigned int)
Line
Count
Source
1304
124k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
124k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
124k
    constexpr auto kind = T::getCellKind();
1307
124k
    cell->setKindAndSize({kind, size});
1308
124k
    return cell;
1309
124k
  }
Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char16_t, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, true>, llvh::ArrayRef<char16_t>&>(void*, unsigned int, llvh::ArrayRef<char16_t>&)
Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char16_t, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, true>, unsigned int&>(void*, unsigned int, unsigned int&)
Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, true>, llvh::ArrayRef<char>&>(void*, unsigned int, llvh::ArrayRef<char>&)
Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, true>, unsigned int&>(void*, unsigned int, unsigned int&)
hermes::vm::DynamicStringPrimitive<char16_t, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, false>, llvh::ArrayRef<char16_t>&>(void*, unsigned int, llvh::ArrayRef<char16_t>&)
Line
Count
Source
1304
14.7k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
14.7k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
14.7k
    constexpr auto kind = T::getCellKind();
1307
14.7k
    cell->setKindAndSize({kind, size});
1308
14.7k
    return cell;
1309
14.7k
  }
hermes::vm::DynamicStringPrimitive<char16_t, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, false>, unsigned int&>(void*, unsigned int, unsigned int&)
Line
Count
Source
1304
124k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
124k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
124k
    constexpr auto kind = T::getCellKind();
1307
124k
    cell->setKindAndSize({kind, size});
1308
124k
    return cell;
1309
124k
  }
hermes::vm::DynamicStringPrimitive<char, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, false>, llvh::ArrayRef<char>&>(void*, unsigned int, llvh::ArrayRef<char>&)
Line
Count
Source
1304
1.03M
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
1.03M
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
1.03M
    constexpr auto kind = T::getCellKind();
1307
1.03M
    cell->setKindAndSize({kind, size});
1308
1.03M
    return cell;
1309
1.03M
  }
hermes::vm::DynamicStringPrimitive<char, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, false>, unsigned int&>(void*, unsigned int, unsigned int&)
Line
Count
Source
1304
125k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
125k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
125k
    constexpr auto kind = T::getCellKind();
1307
125k
    cell->setKindAndSize({kind, size});
1308
125k
    return cell;
1309
125k
  }
hermes::vm::ExternalStringPrimitive<char16_t>* hermes::vm::GCBase::constructCell<hermes::vm::ExternalStringPrimitive<char16_t>, std::__1::basic_string<char16_t, std::__1::char_traits<char16_t>, std::__1::allocator<char16_t> > >(void*, unsigned int, std::__1::basic_string<char16_t, std::__1::char_traits<char16_t>, std::__1::allocator<char16_t> >&&)
Line
Count
Source
1304
202
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
202
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
202
    constexpr auto kind = T::getCellKind();
1307
202
    cell->setKindAndSize({kind, size});
1308
202
    return cell;
1309
202
  }
hermes::vm::ExternalStringPrimitive<char>* hermes::vm::GCBase::constructCell<hermes::vm::ExternalStringPrimitive<char>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(void*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Line
Count
Source
1304
5
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
5
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
5
    constexpr auto kind = T::getCellKind();
1307
5
    cell->setKindAndSize({kind, size});
1308
5
    return cell;
1309
5
  }
hermes::vm::BufferedStringPrimitive<char16_t>* hermes::vm::GCBase::constructCell<hermes::vm::BufferedStringPrimitive<char16_t>, hermes::vm::Runtime&, unsigned int&, hermes::vm::Handle<hermes::vm::ExternalStringPrimitive<char16_t> >&>(void*, unsigned int, hermes::vm::Runtime&, unsigned int&, hermes::vm::Handle<hermes::vm::ExternalStringPrimitive<char16_t> >&)
Line
Count
Source
1304
999
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
999
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
999
    constexpr auto kind = T::getCellKind();
1307
999
    cell->setKindAndSize({kind, size});
1308
999
    return cell;
1309
999
  }
Unexecuted instantiation: hermes::vm::BufferedStringPrimitive<char>* hermes::vm::GCBase::constructCell<hermes::vm::BufferedStringPrimitive<char>, hermes::vm::Runtime&, unsigned int&, hermes::vm::Handle<hermes::vm::ExternalStringPrimitive<char> >&>(void*, unsigned int, hermes::vm::Runtime&, unsigned int&, hermes::vm::Handle<hermes::vm::ExternalStringPrimitive<char> >&)
hermes::vm::SingleObject<(hermes::vm::CellKind)60>* hermes::vm::GCBase::constructCell<hermes::vm::SingleObject<(hermes::vm::CellKind)60>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Line
Count
Source
1304
113
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
113
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
113
    constexpr auto kind = T::getCellKind();
1307
113
    cell->setKindAndSize({kind, size});
1308
113
    return cell;
1309
113
  }
hermes::vm::SingleObject<(hermes::vm::CellKind)59>* hermes::vm::GCBase::constructCell<hermes::vm::SingleObject<(hermes::vm::CellKind)59>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Line
Count
Source
1304
113
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
113
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
113
    constexpr auto kind = T::getCellKind();
1307
113
    cell->setKindAndSize({kind, size});
1308
113
    return cell;
1309
113
  }
hermes::vm::HadesGC::OldGen::FreelistCell* hermes::vm::GCBase::constructCell<hermes::vm::HadesGC::OldGen::FreelistCell>(void*, unsigned int)
Line
Count
Source
1304
184
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
184
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
184
    constexpr auto kind = T::getCellKind();
1307
184
    cell->setKindAndSize({kind, size});
1308
184
    return cell;
1309
184
  }
hermes::vm::DictPropertyMap* hermes::vm::GCBase::constructCell<hermes::vm::DictPropertyMap, unsigned int&, unsigned int&>(void*, unsigned int, unsigned int&, unsigned int&)
Line
Count
Source
1304
245k
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
245k
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
245k
    constexpr auto kind = T::getCellKind();
1307
245k
    cell->setKindAndSize({kind, size});
1308
245k
    return cell;
1309
245k
  }
Unexecuted instantiation: hermes::vm::testhelpers::DummyObject* hermes::vm::GCBase::constructCell<hermes::vm::testhelpers::DummyObject, hermes::vm::HadesGC&>(void*, unsigned int, hermes::vm::HadesGC&)
Unexecuted instantiation: hermes::vm::HashMapEntry* hermes::vm::GCBase::constructCell<hermes::vm::HashMapEntry>(void*, unsigned int)
hermes::vm::OrderedHashMap* hermes::vm::GCBase::constructCell<hermes::vm::OrderedHashMap, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue32> >&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue32> >&)
Line
Count
Source
1304
113
  static T *constructCell(void *ptr, uint32_t size, Args &&...args) {
1305
113
    auto *cell = new (ptr) T(std::forward<Args>(args)...);
1306
113
    constexpr auto kind = T::getCellKind();
1307
113
    cell->setKindAndSize({kind, size});
1308
113
    return cell;
1309
113
  }
Unexecuted instantiation: hermes::vm::JSCallSite* hermes::vm::GCBase::constructCell<hermes::vm::JSCallSite, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::JSError>&, unsigned int&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::JSError>&, unsigned int&)
Unexecuted instantiation: hermes::vm::JSCallableProxy* hermes::vm::GCBase::constructCell<hermes::vm::JSCallableProxy, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&&, hermes::vm::Handle<hermes::vm::HiddenClass>&&)
Unexecuted instantiation: hermes::vm::JSRegExpStringIterator* hermes::vm::GCBase::constructCell<hermes::vm::JSRegExpStringIterator, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::StringPrimitive>&, bool&, bool&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::StringPrimitive>&, bool&, bool&)
1310
1311
  /// Number of finalized objects in the last collection.
1312
  unsigned numFinalizedObjects_{0};
1313
1314
  /// The total number of bytes allocated in the execution.
1315
  uint64_t totalAllocatedBytes_{0};
1316
1317
  /// A trace of GC execution.
1318
  GCExecTrace execTrace_;
1319
1320
/// These fields are not available in optimized builds.
1321
#ifndef NDEBUG
1322
  /// Number of currently allocated objects present in the heap before the start
1323
  /// of the last collection.  Some may be unreachable.
1324
  unsigned numAllocatedObjects_{0};
1325
  /// Number of reachable objects in the last collection.  (More properly, this
1326
  /// is the number not known to be unreachable: if a GC does not consider
1327
  /// determine the reachability of some subset of objects, for example, an old
1328
  /// generation in a generational collection, those objects should be included
1329
  /// in this count.)
1330
  unsigned numReachableObjects_{0};
1331
  /// Number of collected objects in the last collection.  Equal to
1332
  /// numAllocatedObjects_ (at the start of the last collection),
1333
  /// minus numReachableObjects_ found in that collection.
1334
  unsigned numCollectedObjects_{0};
1335
  /// Number of marked symbols.
1336
  unsigned numMarkedSymbols_{0};
1337
  /// Number of hidden classes alive after the last collection.
1338
  unsigned numHiddenClasses_{0};
1339
  /// Number of "leaf" hidden classes alive after the last collection.
1340
  unsigned numLeafHiddenClasses_{0};
1341
1342
  /// Associate a semi-unique (until it overflows) id with every allocation
1343
  /// for easier identification when debugging.
1344
  uint64_t debugAllocationCounter_{0};
1345
#endif
1346
1347
  /// User-supplied callbacks invoked by the GC to query information or perform
1348
  /// tasks.
1349
  GCCallbacks &gcCallbacks_;
1350
1351
  /// Base of all pointers in compressed pointers implementation.
1352
  PointerBase &pointerBase_;
1353
1354
  /// A place to log crash data if a crash is about to occur.
1355
  std::shared_ptr<CrashManager> crashMgr_;
1356
1357
  HeapKind heapKind_;
1358
1359
  /// Callback called once for each GC event that wants to be logged. Can be
1360
  /// null if no analytics are requested.
1361
  std::function<void(const GCAnalyticsEvent &)> analyticsCallback_;
1362
1363
  /// Capture all analytics events to print stats at the end.
1364
  std::vector<GCAnalyticsEvent> analyticsEvents_;
1365
1366
  /// Whether to output GC statistics at the end of execution.
1367
  bool recordGcStats_{false};
1368
1369
  /// Whether or not a GC cycle is currently occurring.
1370
  bool inGC_{false};
1371
1372
  /// Whether the GC has OOMed. Currently only useful for understanding crash
1373
  /// dumps, particularly when HERMESVM_EXCEPTION_ON_OOM is set.
1374
  bool hasOOMed_{false};
1375
1376
  /// The block of fields below records values of various metrics at
1377
  /// the start of execution, so that we can get the values at the end
1378
  /// and subtract.  The "runtimeWillExecute" method is called at
1379
  /// first bytecode execution, but also when executing the bodies of
1380
  /// other bundles.  We want to record these at the first such call.
1381
  /// This field tells whether these values have been recorded yet.
1382
  bool execStartTimeRecorded_{false};
1383
1384
  /// Time at which execution of the Hermes VM began.
1385
  std::chrono::time_point<std::chrono::steady_clock> execStartTime_;
1386
  std::chrono::microseconds execStartCPUTime_;
1387
  /// Number of context switches before execution of the Hermes VM began.
1388
  long startNumVoluntaryContextSwitches_{0};
1389
  long startNumInvoluntaryContextSwitches_{0};
1390
1391
  // The cumulative GC stats.
1392
  CumulativeHeapStats cumStats_;
1393
1394
  /// Name to identify this heap in logs.
1395
  std::string name_;
1396
1397
  /// weakSlots_ is a list of all the weak pointers in the system. They are
1398
  /// invalidated if they point to an object that is dead, and do not count
1399
  /// towards whether an object is live or dead.
1400
  ManagedChunkedList<WeakRefSlot> weakSlots_;
1401
1402
  /// A list of all slots used by WeakMap/WeakSet. They are freed by the mutator
1403
  /// when operating on a WeakMap/WeakSet, or in the finalizer during sweeping.
1404
  /// In collection phase, GC visits each non-free slot to update their values.
1405
  ManagedChunkedList<WeakMapEntrySlot> weakMapEntrySlots_;
1406
1407
  /// Tracks what objects need a stable identity for features such as heap
1408
  /// snapshots and the memory profiler.
1409
  IDTracker idTracker_;
1410
1411
#ifdef HERMES_MEMORY_INSTRUMENTATION
1412
  /// Attaches stack-traces to objects when enabled.
1413
  AllocationLocationTracker allocationLocationTracker_;
1414
1415
  /// Attaches stack-traces to objects when enabled.
1416
  SamplingAllocationLocationTracker samplingAllocationTracker_;
1417
#endif
1418
1419
#ifndef NDEBUG
1420
  /// The number of reasons why no allocation is allowed in this heap right
1421
  /// now.
1422
  uint32_t noAllocLevel_{0};
1423
1424
  friend class NoAllocScope;
1425
#endif
1426
1427
#ifdef HERMESVM_SANITIZE_HANDLES
1428
  /// \return true if we should run handle sanitization and the coin flip with
1429
  /// probability sanitizeRate_ has passed.
1430
  bool shouldSanitizeHandles();
1431
1432
  /// Whether to keep moving the heap around to detect unsanitary GC handles.
1433
  double sanitizeRate_{1.0};
1434
1435
  /// PRNG for sanitizing at a less than 1.0 rate.
1436
  std::minstd_rand randomEngine_;
1437
#else
1438
  /// Sanitize handles is completely disabled (and ignored at runtime) without
1439
  /// a special build mode.
1440
  static constexpr double sanitizeRate_{0.0};
1441
1442
0
  static constexpr bool shouldSanitizeHandles() {
1443
0
    return false;
1444
0
  }
1445
#endif
1446
1447
 private:
1448
#ifdef HERMESVM_GC_RUNTIME
1449
  /// Use the kind tag of the GC to statically call a function with one of the
1450
  /// available runtime GCs.
1451
  template <typename Func>
1452
  auto runtimeGCDispatch(Func f) {
1453
    switch (getKind()) {
1454
#define GC_KIND(kind)          \
1455
  case GCBase::HeapKind::kind: \
1456
    return f(llvh::cast<kind>(this));
1457
      RUNTIME_GC_KINDS
1458
#undef GC_KIND
1459
      default:
1460
        llvm_unreachable("No other valid GC for RuntimeGC");
1461
    }
1462
  }
1463
#endif
1464
1465
  template <typename T, XorPtrKeyID K>
1466
  friend class XorPtr;
1467
1468
  /// Randomly generated key used to obfuscate pointers in XorPtr.
1469
  uintptr_t pointerEncryptionKey_[XorPtrKeyID::_NumKeys];
1470
1471
  /// Callback called if it's not null when the Live Data Tripwire is
1472
  /// triggered.
1473
  std::function<void(GCTripwireContext &)> tripwireCallback_;
1474
1475
  /// Maximum size limit before the heap size tripwire will trigger.
1476
  gcheapsize_t tripwireLimit_;
1477
1478
  /// True if the tripwire has already been called on this heap.
1479
  bool tripwireCalled_{false};
1480
};
1481
1482
// Utilities for formatting time durations and memory sizes.
1483
1484
/// An object that, written to an ostream, formats the given # of
1485
/// secs in appropriate units (down to microseconds).
1486
struct DurationFormatObj {
1487
  double secs;
1488
};
1489
llvh::raw_ostream &operator<<(
1490
    llvh::raw_ostream &os,
1491
    const DurationFormatObj &dfo);
1492
0
inline DurationFormatObj formatSecs(double secs) {
1493
0
  return {secs};
1494
0
}
1495
1496
/// An object that, written to an ostream, formats the given # of
1497
/// bytes in appropriate units (bytes to GiB).
1498
struct SizeFormatObj {
1499
  uint64_t bytes;
1500
};
1501
llvh::raw_ostream &operator<<(llvh::raw_ostream &os, const SizeFormatObj &sfo);
1502
0
inline SizeFormatObj formatSize(uint64_t size) {
1503
0
  return {size};
1504
0
}
1505
1506
} // namespace vm
1507
} // namespace hermes
1508
#pragma GCC diagnostic pop
1509
1510
#endif // HERMES_VM_GCBASE_H