1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "vm/SymbolType.h"
8 
9 #include "builtin/Symbol.h"
10 #include "gc/Allocator.h"
11 #include "gc/HashUtil.h"
12 #include "gc/Rooting.h"
13 #include "util/StringBuffer.h"
14 #include "vm/JSContext.h"
15 #include "vm/Realm.h"
16 
17 #include "vm/Realm-inl.h"
18 
19 using JS::Symbol;
20 using namespace js;
21 
newInternal(JSContext * cx,JS::SymbolCode code,uint32_t hash,HandleAtom description)22 Symbol* Symbol::newInternal(JSContext* cx, JS::SymbolCode code, uint32_t hash,
23                             HandleAtom description) {
24   MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
25   AutoAllocInAtomsZone az(cx);
26 
27   Symbol* p = Allocate<JS::Symbol>(cx);
28   if (!p) {
29     return nullptr;
30   }
31   return new (p) Symbol(code, hash, description);
32 }
33 
new_(JSContext * cx,JS::SymbolCode code,HandleString description)34 Symbol* Symbol::new_(JSContext* cx, JS::SymbolCode code,
35                      HandleString description) {
36   RootedAtom atom(cx);
37   if (description) {
38     atom = AtomizeString(cx, description);
39     if (!atom) {
40       return nullptr;
41     }
42   }
43 
44   Symbol* sym = newInternal(cx, code, cx->runtime()->randomHashCode(), atom);
45   if (sym) {
46     cx->markAtom(sym);
47   }
48   return sym;
49 }
50 
for_(JSContext * cx,HandleString description)51 Symbol* Symbol::for_(JSContext* cx, HandleString description) {
52   RootedAtom atom(cx, AtomizeString(cx, description));
53   if (!atom) {
54     return nullptr;
55   }
56 
57   SymbolRegistry& registry = cx->symbolRegistry();
58   DependentAddPtr<SymbolRegistry> p(cx, registry, atom);
59   if (p) {
60     cx->markAtom(*p);
61     return *p;
62   }
63 
64   // Rehash the hash of the atom to give the corresponding symbol a hash
65   // that is different than the hash of the corresponding atom.
66   HashNumber hash = mozilla::HashGeneric(atom->hash());
67   Symbol* sym = newInternal(cx, SymbolCode::InSymbolRegistry, hash, atom);
68   if (!sym) {
69     return nullptr;
70   }
71 
72   if (!p.add(cx, registry, atom, sym)) {
73     return nullptr;
74   }
75 
76   cx->markAtom(sym);
77   return sym;
78 }
79 
80 #if defined(DEBUG) || defined(JS_JITSPEW)
dump()81 void Symbol::dump() {
82   js::Fprinter out(stderr);
83   dump(out);
84 }
85 
dump(js::GenericPrinter & out)86 void Symbol::dump(js::GenericPrinter& out) {
87   if (isWellKnownSymbol()) {
88     // All the well-known symbol names are ASCII.
89     description()->dumpCharsNoNewline(out);
90   } else if (code_ == SymbolCode::InSymbolRegistry ||
91              code_ == SymbolCode::UniqueSymbol) {
92     out.printf(code_ == SymbolCode::InSymbolRegistry ? "Symbol.for("
93                                                      : "Symbol(");
94 
95     if (description()) {
96       description()->dumpCharsNoNewline(out);
97     } else {
98       out.printf("undefined");
99     }
100 
101     out.putChar(')');
102 
103     if (code_ == SymbolCode::UniqueSymbol) {
104       out.printf("@%p", (void*)this);
105     }
106   } else if (code_ == SymbolCode::PrivateNameSymbol) {
107     MOZ_ASSERT(description());
108     out.putChar('#');
109     description()->dumpCharsNoNewline(out);
110     out.printf("@%p", (void*)this);
111   } else {
112     out.printf("<Invalid Symbol code=%u>", unsigned(code_));
113   }
114 }
115 #endif  // defined(DEBUG) || defined(JS_JITSPEW)
116 
SymbolDescriptiveString(JSContext * cx,Symbol * sym,MutableHandleValue result)117 bool js::SymbolDescriptiveString(JSContext* cx, Symbol* sym,
118                                  MutableHandleValue result) {
119   // steps 2-5
120   JSStringBuilder sb(cx);
121   if (!sb.append("Symbol(")) {
122     return false;
123   }
124   if (JSAtom* desc = sym->description()) {
125     if (!sb.append(desc)) {
126       return false;
127     }
128   }
129   if (!sb.append(')')) {
130     return false;
131   }
132 
133   // step 6
134   JSString* str = sb.finishString();
135   if (!str) {
136     return false;
137   }
138   result.setString(str);
139   return true;
140 }
141 
size(mozilla::MallocSizeOf mallocSizeOf) const142 JS::ubi::Node::Size JS::ubi::Concrete<JS::Symbol>::size(
143     mozilla::MallocSizeOf mallocSizeOf) const {
144   // If we start allocating symbols in the nursery, we will need to update
145   // this method.
146   MOZ_ASSERT(get().isTenured());
147   return js::gc::Arena::thingSize(get().asTenured().getAllocKind());
148 }
149