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