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