summaryrefslogtreecommitdiffstats
path: root/js/src/vm/SymbolType.h
blob: 2a66e6c30f7667a80191dcbf5141820d66811604 (plain)
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 <stdio.h>

#include "gc/Barrier.h"
#include "gc/Tracer.h"
#include "js/AllocPolicy.h"
#include "js/GCHashTable.h"
#include "js/RootingAPI.h"
#include "js/shadow/Symbol.h"  // JS::shadow::Symbol
#include "js/Symbol.h"
#include "js/TypeDecls.h"
#include "vm/StringType.h"

namespace js {
class JS_PUBLIC_API GenericPrinter;
}

namespace JS {

class Symbol
    : public js::gc::CellWithTenuredGCPointer<js::gc::TenuredCell, JSAtom> {
  friend class js::gc::CellAllocator;

 public:
  // User description of symbol, stored in the cell header.
  JSAtom* description() const { return headerPtr(); }

 private:
  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, Handle<JSAtom*> desc)
      : CellWithTenuredGCPointer(desc), code_(code), hash_(hash) {}

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

  static Symbol* newInternal(JSContext* cx, SymbolCode code,
                             js::HashNumber hash, Handle<JSAtom*> 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* newWellKnown(JSContext* cx, SymbolCode code,
                              Handle<js::PropertyName*> description);
  static Symbol* for_(JSContext* cx, js::HandleString 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 ||
           code_ == SymbolCode::isConcatSpreadable;
  }

  // Symbol created for the #PrivateName syntax.
  bool isPrivateName() const { return code_ == SymbolCode::PrivateNameSymbol; }

  static const JS::TraceKind TraceKind = JS::TraceKind::Symbol;

  void traceChildren(JSTracer* trc);
  void finalize(JS::GCContext* gcx) {}

  // Override base class implementation to tell GC about well-known symbols.
  bool isPermanentAndMayBeShared() const { return isWellKnownSymbol(); }

  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

  static constexpr size_t offsetOfHash() { return offsetof(Symbol, hash_); }
};

} /* namespace JS */

namespace js {

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

  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<WeakHeapPtr<JS::Symbol*>, HashSymbolsByDescription,
                       SystemAllocPolicy> {
 public:
  SymbolRegistry() = default;
};

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