Source code

Revision control

Other Tools

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 "builtin/Symbol.h"
8
9
#include "js/Symbol.h"
10
#include "js/PropertySpec.h"
11
#include "util/StringBuffer.h"
12
#include "vm/SymbolType.h"
13
14
#include "vm/JSObject-inl.h"
15
#include "vm/NativeObject-inl.h"
16
17
using JS::Symbol;
18
using namespace js;
19
20
const JSClass SymbolObject::class_ = {
21
"Symbol", JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
22
JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol)};
23
24
SymbolObject* SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol) {
25
SymbolObject* obj = NewBuiltinClassInstance<SymbolObject>(cx);
26
if (!obj) {
27
return nullptr;
28
}
29
obj->setPrimitiveValue(symbol);
30
return obj;
31
}
32
33
const JSPropertySpec SymbolObject::properties[] = {
34
JS_PSG("description", descriptionGetter, 0), JS_PS_END};
35
36
const JSFunctionSpec SymbolObject::methods[] = {
37
JS_FN(js_toString_str, toString, 0, 0),
38
JS_FN(js_valueOf_str, valueOf, 0, 0),
39
JS_SYM_FN(toPrimitive, toPrimitive, 1, JSPROP_READONLY), JS_FS_END};
40
41
const JSFunctionSpec SymbolObject::staticMethods[] = {
42
JS_FN("for", for_, 1, 0), JS_FN("keyFor", keyFor, 1, 0), JS_FS_END};
43
44
JSObject* SymbolObject::initClass(JSContext* cx, Handle<GlobalObject*> global,
45
bool defineMembers) {
46
// This uses &JSObject::class_ because: "The Symbol prototype object is an
47
// ordinary object. It is not a Symbol instance and does not have a
48
// [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3)
49
RootedObject proto(
50
cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
51
if (!proto) {
52
return nullptr;
53
}
54
55
RootedFunction ctor(cx, GlobalObject::createConstructor(
56
cx, construct, ClassName(JSProto_Symbol, cx), 0));
57
if (!ctor) {
58
return nullptr;
59
}
60
61
if (defineMembers) {
62
// Define the well-known symbol properties, such as Symbol.iterator.
63
ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
64
RootedValue value(cx);
65
unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
66
WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
67
for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
68
value.setSymbol(wks->get(i));
69
if (!NativeDefineDataProperty(cx, ctor, names[i], value, attrs)) {
70
return nullptr;
71
}
72
}
73
}
74
75
if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
76
return nullptr;
77
}
78
79
if (defineMembers) {
80
if (!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
81
!DefineToStringTag(cx, proto, cx->names().Symbol) ||
82
!DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods)) {
83
return nullptr;
84
}
85
}
86
87
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor,
88
proto)) {
89
return nullptr;
90
}
91
return proto;
92
}
93
94
// ES6 rev 24 (2014 Apr 27) 19.4.1.1 and 19.4.1.2
95
bool SymbolObject::construct(JSContext* cx, unsigned argc, Value* vp) {
96
// According to a note in the draft standard, "Symbol has ordinary
97
// [[Construct]] behaviour but the definition of its @@create method causes
98
// `new Symbol` to throw a TypeError exception." We do not support @@create
99
// yet, so just throw a TypeError.
100
CallArgs args = CallArgsFromVp(argc, vp);
101
if (args.isConstructing()) {
102
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
103
JSMSG_NOT_CONSTRUCTOR, "Symbol");
104
return false;
105
}
106
107
// steps 1-3
108
RootedString desc(cx);
109
if (!args.get(0).isUndefined()) {
110
desc = ToString(cx, args.get(0));
111
if (!desc) {
112
return false;
113
}
114
}
115
116
// step 4
117
RootedSymbol symbol(cx,
118
JS::Symbol::new_(cx, JS::SymbolCode::UniqueSymbol, desc));
119
if (!symbol) {
120
return false;
121
}
122
args.rval().setSymbol(symbol);
123
return true;
124
}
125
126
// ES6 rev 24 (2014 Apr 27) 19.4.2.2
127
bool SymbolObject::for_(JSContext* cx, unsigned argc, Value* vp) {
128
CallArgs args = CallArgsFromVp(argc, vp);
129
130
// steps 1-2
131
RootedString stringKey(cx, ToString(cx, args.get(0)));
132
if (!stringKey) {
133
return false;
134
}
135
136
// steps 3-7
137
JS::Symbol* symbol = JS::Symbol::for_(cx, stringKey);
138
if (!symbol) {
139
return false;
140
}
141
args.rval().setSymbol(symbol);
142
return true;
143
}
144
145
// ES6 rev 25 (2014 May 22) 19.4.2.7
146
bool SymbolObject::keyFor(JSContext* cx, unsigned argc, Value* vp) {
147
CallArgs args = CallArgsFromVp(argc, vp);
148
149
// step 1
150
HandleValue arg = args.get(0);
151
if (!arg.isSymbol()) {
152
ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, arg,
153
nullptr, "not a symbol");
154
return false;
155
}
156
157
// step 2
158
if (arg.toSymbol()->code() == JS::SymbolCode::InSymbolRegistry) {
159
#ifdef DEBUG
160
RootedString desc(cx, arg.toSymbol()->description());
161
MOZ_ASSERT(Symbol::for_(cx, desc) == arg.toSymbol());
162
#endif
163
args.rval().setString(arg.toSymbol()->description());
164
return true;
165
}
166
167
// step 3: omitted
168
// step 4
169
args.rval().setUndefined();
170
return true;
171
}
172
173
MOZ_ALWAYS_INLINE bool IsSymbol(HandleValue v) {
174
return v.isSymbol() || (v.isObject() && v.toObject().is<SymbolObject>());
175
}
176
177
// ES6 rev 27 (2014 Aug 24) 19.4.3.2
178
bool SymbolObject::toString_impl(JSContext* cx, const CallArgs& args) {
179
// steps 1-3
180
HandleValue thisv = args.thisv();
181
MOZ_ASSERT(IsSymbol(thisv));
182
Rooted<Symbol*> sym(cx, thisv.isSymbol()
183
? thisv.toSymbol()
184
: thisv.toObject().as<SymbolObject>().unbox());
185
186
// step 4
187
return SymbolDescriptiveString(cx, sym, args.rval());
188
}
189
190
bool SymbolObject::toString(JSContext* cx, unsigned argc, Value* vp) {
191
CallArgs args = CallArgsFromVp(argc, vp);
192
return CallNonGenericMethod<IsSymbol, toString_impl>(cx, args);
193
}
194
195
// ES6 rev 24 (2014 Apr 27) 19.4.3.3
196
bool SymbolObject::valueOf_impl(JSContext* cx, const CallArgs& args) {
197
// Step 3, the error case, is handled by CallNonGenericMethod.
198
HandleValue thisv = args.thisv();
199
MOZ_ASSERT(IsSymbol(thisv));
200
if (thisv.isSymbol()) {
201
args.rval().set(thisv);
202
} else {
203
args.rval().setSymbol(thisv.toObject().as<SymbolObject>().unbox());
204
}
205
return true;
206
}
207
208
bool SymbolObject::valueOf(JSContext* cx, unsigned argc, Value* vp) {
209
CallArgs args = CallArgsFromVp(argc, vp);
210
return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
211
}
212
213
// ES6 19.4.3.4
214
bool SymbolObject::toPrimitive(JSContext* cx, unsigned argc, Value* vp) {
215
CallArgs args = CallArgsFromVp(argc, vp);
216
217
// The specification gives exactly the same algorithm for @@toPrimitive as
218
// for valueOf, so reuse the valueOf implementation.
219
return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
220
}
221
222
bool SymbolObject::descriptionGetter_impl(JSContext* cx, const CallArgs& args) {
223
// Get symbol object pointer.
224
HandleValue thisv = args.thisv();
225
MOZ_ASSERT(IsSymbol(thisv));
226
Rooted<Symbol*> sym(cx, thisv.isSymbol()
227
? thisv.toSymbol()
228
: thisv.toObject().as<SymbolObject>().unbox());
229
230
// Return the symbol's description if present, otherwise return undefined.
231
if (JSString* str = sym->description()) {
232
args.rval().setString(str);
233
} else {
234
args.rval().setUndefined();
235
}
236
return true;
237
}
238
239
bool SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp) {
240
CallArgs args = CallArgsFromVp(argc, vp);
241
return CallNonGenericMethod<IsSymbol, descriptionGetter_impl>(cx, args);
242
}
243
244
JSObject* js::InitSymbolClass(JSContext* cx, Handle<GlobalObject*> global) {
245
return SymbolObject::initClass(cx, global, true);
246
}
247
248
JSObject* js::InitBareSymbolCtor(JSContext* cx, Handle<GlobalObject*> global) {
249
return SymbolObject::initClass(cx, global, false);
250
}