DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * GC-internal definitions.
 */

#ifndef gc_GCInternals_h
#define gc_GCInternals_h

#include "mozilla/ArrayUtils.h"
#include "mozilla/Maybe.h"

#include "gc/GC.h"
#include "vm/JSContext.h"

namespace js {
namespace gc {

class MOZ_RAII AutoCheckCanAccessAtomsDuringGC {
#ifdef DEBUG
  JSRuntime* runtime;

 public:
  explicit AutoCheckCanAccessAtomsDuringGC(JSRuntime* rt) : runtime(rt) {
    // Ensure we're only used from within the GC.
    MOZ_ASSERT(JS::RuntimeHeapIsMajorCollecting());

    // Ensure there is no off-thread parsing running.
    MOZ_ASSERT(!rt->hasHelperThreadZones());

    // Set up a check to assert if we try to start an off-thread parse.
    runtime->setOffThreadParsingBlocked(true);
  }
  ~AutoCheckCanAccessAtomsDuringGC() {
    runtime->setOffThreadParsingBlocked(false);
  }
#else
 public:
  explicit AutoCheckCanAccessAtomsDuringGC(JSRuntime* rt) {}
#endif
};

// Abstract base class for exclusive heap access for tracing or GC.
class MOZ_RAII AutoHeapSession {
 public:
  ~AutoHeapSession();

 protected:
  AutoHeapSession(JSRuntime* rt, JS::HeapState state);

 private:
  AutoHeapSession(const AutoHeapSession&) = delete;
  void operator=(const AutoHeapSession&) = delete;

  JSRuntime* runtime;
  JS::HeapState prevState;
  AutoGeckoProfilerEntry profilingStackFrame;
};

class MOZ_RAII AutoGCSession : public AutoHeapSession {
 public:
  explicit AutoGCSession(JSRuntime* rt, JS::HeapState state)
      : AutoHeapSession(rt, state) {}

  AutoCheckCanAccessAtomsDuringGC& checkAtomsAccess() {
    return maybeCheckAtomsAccess.ref();
  }

  // During a GC we can check that it's not possible for anything else to be
  // using the atoms zone.
  mozilla::Maybe<AutoCheckCanAccessAtomsDuringGC> maybeCheckAtomsAccess;
};

class MOZ_RAII AutoTraceSession : public AutoLockAllAtoms,
                                  public AutoHeapSession {
 public:
  explicit AutoTraceSession(JSRuntime* rt)
      : AutoLockAllAtoms(rt), AutoHeapSession(rt, JS::HeapState::Tracing) {}
};

struct MOZ_RAII AutoFinishGC {
  explicit AutoFinishGC(JSContext* cx, JS::GCReason reason) {
    FinishGC(cx, reason);
  }
};

// This class should be used by any code that needs exclusive access to the heap
// in order to trace through it.
class MOZ_RAII AutoPrepareForTracing : private AutoFinishGC,
                                       public AutoTraceSession {
 public:
  explicit AutoPrepareForTracing(JSContext* cx)
      : AutoFinishGC(cx, JS::GCReason::PREPARE_FOR_TRACING),
        AutoTraceSession(cx->runtime()) {}
};

AbortReason IsIncrementalGCUnsafe(JSRuntime* rt);

#ifdef JS_GC_ZEAL

class MOZ_RAII AutoStopVerifyingBarriers {
  GCRuntime* gc;
  bool restartPreVerifier;

 public:
  AutoStopVerifyingBarriers(JSRuntime* rt, bool isShutdown) : gc(&rt->gc) {
    if (gc->isVerifyPreBarriersEnabled()) {
      gc->endVerifyPreBarriers();
      restartPreVerifier = !isShutdown;
    } else {
      restartPreVerifier = false;
    }
  }

  ~AutoStopVerifyingBarriers() {
    // Nasty special case: verification runs a minor GC, which *may* nest
    // inside of an outer minor GC. This is not allowed by the
    // gc::Statistics phase tree. So we pause the "real" GC, if in fact one
    // is in progress.
    gcstats::PhaseKind outer = gc->stats().currentPhaseKind();
    if (outer != gcstats::PhaseKind::NONE) {
      gc->stats().endPhase(outer);
    }
    MOZ_ASSERT(gc->stats().currentPhaseKind() == gcstats::PhaseKind::NONE);

    if (restartPreVerifier) {
      gc->startVerifyPreBarriers();
    }

    if (outer != gcstats::PhaseKind::NONE) {
      gc->stats().beginPhase(outer);
    }
  }
};
#else
struct MOZ_RAII AutoStopVerifyingBarriers {
  AutoStopVerifyingBarriers(JSRuntime*, bool) {}
};
#endif /* JS_GC_ZEAL */

#ifdef JSGC_HASH_TABLE_CHECKS
void CheckHashTablesAfterMovingGC(JSRuntime* rt);
void CheckHeapAfterGC(JSRuntime* rt);
#endif

struct MovingTracer final : public JS::CallbackTracer {
  explicit MovingTracer(JSRuntime* rt)
      : CallbackTracer(rt, TraceWeakMapKeysValues) {}

  bool onObjectEdge(JSObject** objp) override;
  bool onShapeEdge(Shape** shapep) override;
  bool onStringEdge(JSString** stringp) override;
  bool onScriptEdge(JSScript** scriptp) override;
  bool onLazyScriptEdge(LazyScript** lazyp) override;
  bool onBaseShapeEdge(BaseShape** basep) override;
  bool onScopeEdge(Scope** scopep) override;
  bool onRegExpSharedEdge(RegExpShared** sharedp) override;
  bool onBigIntEdge(BigInt** bip) override;
  bool onChild(const JS::GCCellPtr& thing) override {
    MOZ_ASSERT(!thing.asCell()->isForwarded());
    return true;
  }

#ifdef DEBUG
  TracerKind getTracerKind() const override { return TracerKind::Moving; }
#endif

 private:
  template <typename T>
  bool updateEdge(T** thingp);
};

struct SweepingTracer final : public JS::CallbackTracer {
  explicit SweepingTracer(JSRuntime* rt)
      : CallbackTracer(rt, TraceWeakMapKeysValues) {}

  bool onObjectEdge(JSObject** objp) override;
  bool onShapeEdge(Shape** shapep) override;
  bool onStringEdge(JSString** stringp) override;
  bool onScriptEdge(JSScript** scriptp) override;
  bool onLazyScriptEdge(LazyScript** lazyp) override;
  bool onBaseShapeEdge(BaseShape** basep) override;
  bool onScopeEdge(Scope** scopep) override;
  bool onRegExpSharedEdge(RegExpShared** sharedp) override;
  bool onBigIntEdge(BigInt** bip) override;
  bool onChild(const JS::GCCellPtr& thing) override {
    MOZ_CRASH("unexpected edge.");
    return true;
  }

#ifdef DEBUG
  TracerKind getTracerKind() const override { return TracerKind::Sweeping; }
#endif

 private:
  template <typename T>
  bool sweepEdge(T** thingp);
};

// Structure for counting how many times objects in a particular group have
// been tenured during a minor collection.
struct TenureCount {
  ObjectGroup* group;
  unsigned count;

  // ObjectGroups are never nursery-allocated, and TenureCounts are only used
  // in minor GC (not compacting GC), so prevent the analysis from
  // complaining about TenureCounts being held live across a minor GC.
} JS_HAZ_NON_GC_POINTER;

// Keep rough track of how many times we tenure objects in particular groups
// during minor collections, using a fixed size hash for efficiency at the cost
// of potential collisions.
struct TenureCountCache {
  static const size_t EntryShift = 4;
  static const size_t EntryCount = 1 << EntryShift;

  TenureCount entries[EntryCount] = {};  // zeroes

  TenureCountCache() = default;

  HashNumber hash(ObjectGroup* group) {
#if JS_BITS_PER_WORD == 32
    static const size_t ZeroBits = 3;
#else
    static const size_t ZeroBits = 4;
#endif

    uintptr_t word = uintptr_t(group);
    MOZ_ASSERT((word & ((1 << ZeroBits) - 1)) == 0);
    word >>= ZeroBits;
    return HashNumber((word >> EntryShift) ^ word);
  }

  TenureCount& findEntry(ObjectGroup* group) {
    return entries[hash(group) % EntryCount];
  }
};

struct MOZ_RAII AutoAssertNoNurseryAlloc {
#ifdef DEBUG
  AutoAssertNoNurseryAlloc();
  ~AutoAssertNoNurseryAlloc();
#else
  AutoAssertNoNurseryAlloc() {}
#endif
};

// Note that this class does not suppress buffer allocation/reallocation in the
// nursery, only Cells themselves.
class MOZ_RAII AutoSuppressNurseryCellAlloc {
  JSContext* cx_;

 public:
  explicit AutoSuppressNurseryCellAlloc(JSContext* cx) : cx_(cx) {
    cx_->nurserySuppressions_++;
  }
  ~AutoSuppressNurseryCellAlloc() { cx_->nurserySuppressions_--; }
};

/*
 * There are a couple of classes here that serve mostly as "tokens" indicating
 * that a condition holds. Some functions force the caller to possess such a
 * token because they would misbehave if the condition were false, and it is
 * far more clear to make the condition visible at the point where it can be
 * affected rather than just crashing in an assertion down in the place where
 * it is relied upon.
 */

/*
 * A class that serves as a token that the nursery in the current thread's zone
 * group is empty.
 */
class MOZ_RAII AutoAssertEmptyNursery {
 protected:
  JSContext* cx;

  mozilla::Maybe<AutoAssertNoNurseryAlloc> noAlloc;

  // Check that the nursery is empty.
  void checkCondition(JSContext* cx);

  // For subclasses that need to empty the nursery in their constructors.
  AutoAssertEmptyNursery() : cx(nullptr) {}

 public:
  explicit AutoAssertEmptyNursery(JSContext* cx) : cx(nullptr) {
    checkCondition(cx);
  }

  AutoAssertEmptyNursery(const AutoAssertEmptyNursery& other)
      : AutoAssertEmptyNursery(other.cx) {}
};

/*
 * Evict the nursery upon construction. Serves as a token indicating that the
 * nursery is empty. (See AutoAssertEmptyNursery, above.)
 *
 * Note that this is very improper subclass of AutoAssertHeapBusy, in that the
 * heap is *not* busy within the scope of an AutoEmptyNursery. I will most
 * likely fix this by removing AutoAssertHeapBusy, but that is currently
 * waiting on jonco's review.
 */
class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery {
 public:
  explicit AutoEmptyNursery(JSContext* cx);
};

extern void DelayCrossCompartmentGrayMarking(JSObject* src);

inline bool IsOOMReason(JS::GCReason reason) {
  return reason == JS::GCReason::LAST_DITCH ||
         reason == JS::GCReason::MEM_PRESSURE;
}

TenuredCell* AllocateCellInGC(JS::Zone* zone, AllocKind thingKind);

} /* namespace gc */
} /* namespace js */

#endif /* gc_GCInternals_h */