summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Symbol.cpp
blob: 29bd5725b3ee13a29267abdfae0b3e20e9ead3b3 (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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * 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/. */

#include "vm/Symbol.h"

#include "jscntxt.h"
#include "jscompartment.h"

#include "builtin/SymbolObject.h"
#include "gc/Allocator.h"
#include "gc/Rooting.h"
#include "vm/StringBuffer.h"

#include "jscompartmentinlines.h"

using JS::Symbol;
using namespace js;

Symbol*
Symbol::newInternal(ExclusiveContext* cx, JS::SymbolCode code, uint32_t hash, JSAtom* description,
                    AutoLockForExclusiveAccess& lock)
{
    MOZ_ASSERT(cx->compartment() == cx->atomsCompartment(lock));

    // Following js::AtomizeString, we grudgingly forgo last-ditch GC here.
    Symbol* p = Allocate<JS::Symbol, NoGC>(cx);
    if (!p) {
        ReportOutOfMemory(cx);
        return nullptr;
    }
    return new (p) Symbol(code, hash, description);
}

Symbol*
Symbol::new_(ExclusiveContext* cx, JS::SymbolCode code, JSString* description)
{
    JSAtom* atom = nullptr;
    if (description) {
        atom = AtomizeString(cx, description);
        if (!atom)
            return nullptr;
    }

    // Lock to allocate. If symbol allocation becomes a bottleneck, this can
    // probably be replaced with an assertion that we're on the main thread.
    AutoLockForExclusiveAccess lock(cx);
    AutoCompartment ac(cx, cx->atomsCompartment(lock), &lock);
    return newInternal(cx, code, cx->compartment()->randomHashCode(), atom, lock);
}

Symbol*
Symbol::for_(js::ExclusiveContext* cx, HandleString description)
{
    JSAtom* atom = AtomizeString(cx, description);
    if (!atom)
        return nullptr;

    AutoLockForExclusiveAccess lock(cx);

    SymbolRegistry& registry = cx->symbolRegistry(lock);
    SymbolRegistry::AddPtr p = registry.lookupForAdd(atom);
    if (p)
        return *p;

    AutoCompartment ac(cx, cx->atomsCompartment(lock), &lock);
    Symbol* sym = newInternal(cx, SymbolCode::InSymbolRegistry, atom->hash(), atom, lock);
    if (!sym)
        return nullptr;

    // p is still valid here because we have held the lock since the
    // lookupForAdd call, and newInternal can't GC.
    if (!registry.add(p, sym)) {
        // SystemAllocPolicy does not report OOM.
        ReportOutOfMemory(cx);
        return nullptr;
    }
    return sym;
}

#ifdef DEBUG
void
Symbol::dump(FILE* fp)
{
    if (isWellKnownSymbol()) {
        // All the well-known symbol names are ASCII.
        description_->dumpCharsNoNewline(fp);
    } else if (code_ == SymbolCode::InSymbolRegistry || code_ == SymbolCode::UniqueSymbol) {
        fputs(code_ == SymbolCode::InSymbolRegistry ? "Symbol.for(" : "Symbol(", fp);

        if (description_)
            description_->dumpCharsNoNewline(fp);
        else
            fputs("undefined", fp);

        fputc(')', fp);

        if (code_ == SymbolCode::UniqueSymbol)
            fprintf(fp, "@%p", (void*) this);
    } else {
        fprintf(fp, "<Invalid Symbol code=%u>", unsigned(code_));
    }
}
#endif  // DEBUG

bool
js::SymbolDescriptiveString(JSContext* cx, Symbol* sym, MutableHandleValue result)
{
    // steps 2-5
    StringBuffer sb(cx);
    if (!sb.append("Symbol("))
        return false;
    RootedString str(cx, sym->description());
    if (str) {
        if (!sb.append(str))
            return false;
    }
    if (!sb.append(')'))
        return false;

    // step 6
    str = sb.finishString();
    if (!str)
        return false;
    result.setString(str);
    return true;
}

bool
js::IsSymbolOrSymbolWrapper(const Value& v)
{
    return v.isSymbol() || (v.isObject() && v.toObject().is<SymbolObject>());
}

JS::Symbol*
js::ToSymbolPrimitive(const Value& v)
{
    MOZ_ASSERT(IsSymbolOrSymbolWrapper(v));
    return v.isSymbol() ? v.toSymbol() : v.toObject().as<SymbolObject>().unbox();
}


JS::ubi::Node::Size
JS::ubi::Concrete<JS::Symbol>::size(mozilla::MallocSizeOf mallocSizeOf) const
{
    // If we start allocating symbols in the nursery, we will need to update
    // this method.
    MOZ_ASSERT(get().isTenured());
    return js::gc::Arena::thingSize(get().asTenured().getAllocKind());
}