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.

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
/* -*- 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/. */

#ifndef vm_SymbolType_h
#define vm_SymbolType_h

#include "mozilla/Attributes.h"

#include <stdio.h>

#include "jsapi.h"

#include "gc/Barrier.h"
#include "gc/Tracer.h"
#include "js/AllocPolicy.h"
#include "js/GCHashTable.h"
#include "js/HeapAPI.h"
#include "js/RootingAPI.h"
#include "js/Symbol.h"
#include "js/TypeDecls.h"
#include "js/Utility.h"
#include "vm/Printer.h"
#include "vm/StringType.h"

namespace js {
class AutoAccessAtomsZone;
}  // namespace js

namespace JS {

class Symbol : public js::gc::TenuredCell {
 private:
  // User description of symbol. Also meets gc::Cell requirements.
  JSAtom* description_;

  SymbolCode code_;

  // Each Symbol gets its own hash code so that we don't have to use
  // addresses as hash codes (a security hazard).
  js::HashNumber hash_;

  Symbol(SymbolCode code, js::HashNumber hash, JSAtom* desc)
      : description_(desc), code_(code), hash_(hash) {}

  Symbol(const Symbol&) = delete;
  void operator=(const Symbol&) = delete;

  static Symbol* newInternal(JSContext* cx, SymbolCode code,
                             js::HashNumber hash, js::HandleAtom description);

  static void staticAsserts() {
    static_assert(uint32_t(SymbolCode::WellKnownAPILimit) ==
                      JS::shadow::Symbol::WellKnownAPILimit,
                  "JS::shadow::Symbol::WellKnownAPILimit must match "
                  "SymbolCode::WellKnownAPILimit");
    static_assert(
        offsetof(Symbol, code_) == offsetof(JS::shadow::Symbol, code_),
        "JS::shadow::Symbol::code_ offset must match SymbolCode::code_");
  }

 public:
  static Symbol* new_(JSContext* cx, SymbolCode code,
                      js::HandleString description);
  static Symbol* for_(JSContext* cx, js::HandleString description);

  JSAtom* description() const { return description_; }
  SymbolCode code() const { return code_; }
  js::HashNumber hash() const { return hash_; }

  bool isWellKnownSymbol() const {
    return uint32_t(code_) < WellKnownSymbolLimit;
  }

  // An "interesting symbol" is a well-known symbol, like @@toStringTag,
  // that's often looked up on random objects but is usually not present. We
  // optimize this by setting a flag on the object's BaseShape when such
  // symbol properties are added, so we can optimize lookups on objects that
  // don't have the BaseShape flag.
  bool isInterestingSymbol() const {
    return code_ == SymbolCode::toStringTag || code_ == SymbolCode::toPrimitive;
  }

  static const JS::TraceKind TraceKind = JS::TraceKind::Symbol;
  inline void traceChildren(JSTracer* trc) {
    if (description_) {
      js::TraceManuallyBarrieredEdge(trc, &description_, "description");
    }
  }
  inline void finalize(JSFreeOp*) {}

  static MOZ_ALWAYS_INLINE void writeBarrierPre(Symbol* thing) {
    if (thing && !thing->isWellKnownSymbol()) {
      thing->asTenured().writeBarrierPre(thing);
    }
  }

  size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    return mallocSizeOf(this);
  }

#if defined(DEBUG) || defined(JS_JITSPEW)
  void dump();  // Debugger-friendly stderr dump.
  void dump(js::GenericPrinter& out);
#endif
};

} /* namespace JS */

namespace js {

/* Hash policy used by the SymbolRegistry. */
struct HashSymbolsByDescription {
  typedef JS::Symbol* Key;
  typedef JSAtom* Lookup;

  static HashNumber hash(Lookup l) { return HashNumber(l->hash()); }
  static bool match(Key sym, Lookup l) { return sym->description() == l; }
};

/*
 * [SMDOC] Symbol.for() registry (ES6 GlobalSymbolRegistry)
 *
 * The runtime-wide symbol registry, used to implement Symbol.for().
 *
 * ES6 draft rev 25 (2014 May 22) calls this the GlobalSymbolRegistry List. In
 * our implementation, it is not global. There is one per JSRuntime. The
 * symbols in the symbol registry, like all symbols, are allocated in the atoms
 * compartment and can be directly referenced from any compartment. They are
 * never shared across runtimes.
 *
 * The memory management strategy here is modeled after js::AtomSet. It's like
 * a WeakSet. The registry itself does not keep any symbols alive; when a
 * symbol in the registry is collected, the registry entry is removed. No GC
 * nondeterminism is exposed to scripts, because there is no API for
 * enumerating the symbol registry, querying its size, etc.
 */
class SymbolRegistry
    : public GCHashSet<WeakHeapPtrSymbol, HashSymbolsByDescription,
                       SystemAllocPolicy> {
 public:
  SymbolRegistry() {}
};

// ES6 rev 27 (2014 Aug 24) 19.4.3.3
bool SymbolDescriptiveString(JSContext* cx, JS::Symbol* sym,
                             JS::MutableHandleValue result);

} /* namespace js */

#endif /* vm_SymbolType_h */