tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }