tor-browser

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

Object.cpp (72682B)


      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/Object.h"
      8 #include "js/Object.h"  // JS::GetBuiltinClass
      9 
     10 #include "mozilla/Maybe.h"
     11 #include "mozilla/Range.h"
     12 #include "mozilla/RangedPtr.h"
     13 
     14 #include <algorithm>
     15 #include <string_view>
     16 
     17 #include "jsapi.h"
     18 
     19 #include "builtin/Eval.h"
     20 #include "builtin/SelfHostingDefines.h"
     21 #include "gc/GC.h"
     22 #include "jit/InlinableNatives.h"
     23 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     24 #include "js/friend/StackLimits.h"    // js::AutoCheckRecursionLimit
     25 #include "js/PropertySpec.h"
     26 #include "js/UniquePtr.h"
     27 #include "util/Identifier.h"  // js::IsIdentifier
     28 #include "util/StringBuilder.h"
     29 #include "util/Text.h"
     30 #include "vm/BooleanObject.h"
     31 #include "vm/DateObject.h"
     32 #include "vm/EqualityOperations.h"  // js::SameValue
     33 #include "vm/ErrorObject.h"
     34 #include "vm/Iteration.h"
     35 #include "vm/JSContext.h"
     36 #include "vm/NumberObject.h"
     37 #include "vm/PlainObject.h"  // js::PlainObject
     38 #include "vm/RegExpObject.h"
     39 #include "vm/StringObject.h"
     40 #include "vm/StringType.h"
     41 #include "vm/ToSource.h"  // js::ValueToSource
     42 #include "vm/Watchtower.h"
     43 #include "vm/GeckoProfiler-inl.h"
     44 #include "vm/JSObject-inl.h"
     45 #include "vm/NativeObject-inl.h"
     46 #include "vm/Shape-inl.h"
     47 
     48 #ifdef FUZZING
     49 #  include "builtin/TestingFunctions.h"
     50 #endif
     51 
     52 using namespace js;
     53 
     54 using mozilla::Maybe;
     55 using mozilla::Range;
     56 using mozilla::RangedPtr;
     57 
     58 static PlainObject* CreateThis(JSContext* cx, HandleObject newTarget) {
     59  RootedObject proto(cx);
     60  if (!GetPrototypeFromConstructor(cx, newTarget, JSProto_Object, &proto)) {
     61    return nullptr;
     62  }
     63 
     64  gc::AllocKind allocKind = NewObjectGCKind();
     65 
     66  if (proto) {
     67    return NewPlainObjectWithProtoAndAllocKind(cx, proto, allocKind);
     68  }
     69  return NewPlainObjectWithAllocKind(cx, allocKind);
     70 }
     71 
     72 bool js::obj_construct(JSContext* cx, unsigned argc, Value* vp) {
     73  CallArgs args = CallArgsFromVp(argc, vp);
     74 
     75  JSObject* obj;
     76  if (args.isConstructing() &&
     77      (&args.newTarget().toObject() != &args.callee())) {
     78    RootedObject newTarget(cx, &args.newTarget().toObject());
     79    obj = CreateThis(cx, newTarget);
     80  } else if (args.length() > 0 && !args[0].isNullOrUndefined()) {
     81    obj = ToObject(cx, args[0]);
     82  } else {
     83    /* Make an object whether this was called with 'new' or not. */
     84    gc::AllocKind allocKind = NewObjectGCKind();
     85    obj = NewPlainObjectWithAllocKind(cx, allocKind);
     86  }
     87  if (!obj) {
     88    return false;
     89  }
     90 
     91  args.rval().setObject(*obj);
     92  return true;
     93 }
     94 
     95 /* ES5 15.2.4.7. */
     96 bool js::obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp) {
     97  CallArgs args = CallArgsFromVp(argc, vp);
     98 
     99  HandleValue idValue = args.get(0);
    100 
    101  // As an optimization, provide a fast path when rooting is not necessary and
    102  // we can safely retrieve the attributes from the object's shape.
    103 
    104  /* Steps 1-2. */
    105  jsid id;
    106  if (args.thisv().isObject() && idValue.isPrimitive() &&
    107      PrimitiveValueToId<NoGC>(cx, idValue, &id)) {
    108    JSObject* obj = &args.thisv().toObject();
    109 
    110    /* Step 3. */
    111    PropertyResult prop;
    112    if (obj->is<NativeObject>() &&
    113        NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id,
    114                                      &prop)) {
    115      /* Step 4. */
    116      if (prop.isNotFound()) {
    117        args.rval().setBoolean(false);
    118        return true;
    119      }
    120 
    121      /* Step 5. */
    122      JS::PropertyAttributes attrs = GetPropertyAttributes(obj, prop);
    123      args.rval().setBoolean(attrs.enumerable());
    124      return true;
    125    }
    126  }
    127 
    128  /* Step 1. */
    129  RootedId idRoot(cx);
    130  if (!ToPropertyKey(cx, idValue, &idRoot)) {
    131    return false;
    132  }
    133 
    134  /* Step 2. */
    135  RootedObject obj(cx, ToObject(cx, args.thisv()));
    136  if (!obj) {
    137    return false;
    138  }
    139 
    140  /* Step 3. */
    141  Rooted<Maybe<PropertyDescriptor>> desc(cx);
    142  if (!GetOwnPropertyDescriptor(cx, obj, idRoot, &desc)) {
    143    return false;
    144  }
    145 
    146  /* Step 4. */
    147  if (desc.isNothing()) {
    148    args.rval().setBoolean(false);
    149    return true;
    150  }
    151 
    152  /* Step 5. */
    153  args.rval().setBoolean(desc->enumerable());
    154  return true;
    155 }
    156 
    157 static bool obj_toSource(JSContext* cx, unsigned argc, Value* vp) {
    158  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object.prototype", "toSource");
    159  CallArgs args = CallArgsFromVp(argc, vp);
    160 
    161  AutoCheckRecursionLimit recursion(cx);
    162  if (!recursion.check(cx)) {
    163    return false;
    164  }
    165 
    166  RootedObject obj(cx, ToObject(cx, args.thisv()));
    167  if (!obj) {
    168    return false;
    169  }
    170 
    171  JSString* str = ObjectToSource(cx, obj);
    172  if (!str) {
    173    return false;
    174  }
    175 
    176  args.rval().setString(str);
    177  return true;
    178 }
    179 
    180 template <typename CharT>
    181 static bool Consume(RangedPtr<const CharT>& s, RangedPtr<const CharT> e,
    182                    std::string_view chars) {
    183  MOZ_ASSERT(s <= e);
    184  size_t len = chars.length();
    185  if (e - s < len) {
    186    return false;
    187  }
    188  if (!EqualChars(s.get(), chars.data(), len)) {
    189    return false;
    190  }
    191  s += len;
    192  return true;
    193 }
    194 
    195 template <typename CharT>
    196 static bool ConsumeUntil(RangedPtr<const CharT>& s, RangedPtr<const CharT> e,
    197                         char16_t ch) {
    198  MOZ_ASSERT(s <= e);
    199  const CharT* result = js_strchr_limit(s.get(), ch, e.get());
    200  if (!result) {
    201    return false;
    202  }
    203  s += result - s.get();
    204  MOZ_ASSERT(*s == ch);
    205  return true;
    206 }
    207 
    208 template <typename CharT>
    209 static void ConsumeSpaces(RangedPtr<const CharT>& s, RangedPtr<const CharT> e) {
    210  while (s < e && *s == ' ') {
    211    s++;
    212  }
    213 }
    214 
    215 /*
    216 * Given a function source string, return the offset and length of the part
    217 * between '(function $name' and ')'.
    218 */
    219 template <typename CharT>
    220 static bool ArgsAndBodySubstring(Range<const CharT> chars, size_t* outOffset,
    221                                 size_t* outLen) {
    222  const RangedPtr<const CharT> start = chars.begin();
    223  RangedPtr<const CharT> s = start;
    224  RangedPtr<const CharT> e = chars.end();
    225 
    226  if (s == e) {
    227    return false;
    228  }
    229 
    230  // Remove enclosing parentheses.
    231  if (*s == '(' && *(e - 1) == ')') {
    232    s++;
    233    e--;
    234  }
    235 
    236  // Support the following cases, with spaces between tokens:
    237  //
    238  //   -+---------+-+------------+-+-----+-+- [ - <any> - ] - ( -+-
    239  //    |         | |            | |     | |                     |
    240  //    +- async -+ +- function -+ +- * -+ +- <any> - ( ---------+
    241  //                |            |
    242  //                +- get ------+
    243  //                |            |
    244  //                +- set ------+
    245  //
    246  // This accepts some invalid syntax, but we don't care, since it's only
    247  // used by the non-standard toSource, and we're doing a best-effort attempt
    248  // here.
    249 
    250  (void)Consume(s, e, "async");
    251  ConsumeSpaces(s, e);
    252  (void)(Consume(s, e, "function") || Consume(s, e, "get") ||
    253         Consume(s, e, "set"));
    254  ConsumeSpaces(s, e);
    255  (void)Consume(s, e, "*");
    256  ConsumeSpaces(s, e);
    257 
    258  // Jump over the function's name.
    259  if (Consume(s, e, "[")) {
    260    if (!ConsumeUntil(s, e, ']')) {
    261      return false;
    262    }
    263    s++;  // Skip ']'.
    264    ConsumeSpaces(s, e);
    265    if (s >= e || *s != '(') {
    266      return false;
    267    }
    268  } else {
    269    if (!ConsumeUntil(s, e, '(')) {
    270      return false;
    271    }
    272  }
    273 
    274  MOZ_ASSERT(*s == '(');
    275 
    276  *outOffset = s - start;
    277  *outLen = e - s;
    278  MOZ_ASSERT(*outOffset + *outLen <= chars.length());
    279  return true;
    280 }
    281 
    282 enum class PropertyKind { Getter, Setter, Method, Normal };
    283 
    284 JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) {
    285  /* If outermost, we need parentheses to be an expression, not a block. */
    286  bool outermost = cx->cycleDetectorVector().empty();
    287 
    288  AutoCycleDetector detector(cx, obj);
    289  if (!detector.init()) {
    290    return nullptr;
    291  }
    292  if (detector.foundCycle()) {
    293    return NewStringCopyZ<CanGC>(cx, "{}");
    294  }
    295 
    296  JSStringBuilder buf(cx);
    297  if (outermost && !buf.append('(')) {
    298    return nullptr;
    299  }
    300  if (!buf.append('{')) {
    301    return nullptr;
    302  }
    303 
    304  RootedIdVector idv(cx);
    305  if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv)) {
    306    return nullptr;
    307  }
    308 
    309  bool comma = false;
    310 
    311  auto AddProperty = [cx, &comma, &buf](HandleId id, HandleValue val,
    312                                        PropertyKind kind) -> bool {
    313    /* Convert id to a string. */
    314    RootedString idstr(cx);
    315    if (id.isSymbol()) {
    316      RootedValue v(cx, SymbolValue(id.toSymbol()));
    317      idstr = ValueToSource(cx, v);
    318      if (!idstr) {
    319        return false;
    320      }
    321    } else {
    322      RootedValue idv(cx, IdToValue(id));
    323      idstr = ToString<CanGC>(cx, idv);
    324      if (!idstr) {
    325        return false;
    326      }
    327 
    328      /*
    329       * If id is a string that's not an identifier, or if it's a
    330       * negative integer, then it must be quoted.
    331       */
    332      if (id.isAtom() ? !IsIdentifier(id.toAtom()) : id.toInt() < 0) {
    333        UniqueChars quotedId = QuoteString(cx, idstr, '\'');
    334        if (!quotedId) {
    335          return false;
    336        }
    337        idstr = NewStringCopyZ<CanGC>(cx, quotedId.get());
    338        if (!idstr) {
    339          return false;
    340        }
    341      }
    342    }
    343 
    344    RootedString valsource(cx, ValueToSource(cx, val));
    345    if (!valsource) {
    346      return false;
    347    }
    348 
    349    Rooted<JSLinearString*> valstr(cx, valsource->ensureLinear(cx));
    350    if (!valstr) {
    351      return false;
    352    }
    353 
    354    if (comma && !buf.append(", ")) {
    355      return false;
    356    }
    357    comma = true;
    358 
    359    size_t voffset, vlength;
    360 
    361    // Methods and accessors can return exact syntax of source, that fits
    362    // into property without adding property name or "get"/"set" prefix.
    363    // Use the exact syntax when the following conditions are met:
    364    //
    365    //   * It's a function object
    366    //     (exclude proxies)
    367    //   * Function's kind and property's kind are same
    368    //     (this can be false for dynamically defined properties)
    369    //   * Function has explicit name
    370    //     (this can be false for computed property and dynamically defined
    371    //      properties)
    372    //   * Function's name and property's name are same
    373    //     (this can be false for dynamically defined properties)
    374    if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
    375        kind == PropertyKind::Method) {
    376      RootedFunction fun(cx);
    377      if (val.toObject().is<JSFunction>()) {
    378        fun = &val.toObject().as<JSFunction>();
    379        // Method's case should be checked on caller.
    380        if (((fun->isGetter() && kind == PropertyKind::Getter &&
    381              !fun->isAccessorWithLazyName()) ||
    382             (fun->isSetter() && kind == PropertyKind::Setter &&
    383              !fun->isAccessorWithLazyName()) ||
    384             kind == PropertyKind::Method) &&
    385            fun->fullExplicitName()) {
    386          bool result;
    387          if (!EqualStrings(cx, fun->fullExplicitName(), idstr, &result)) {
    388            return false;
    389          }
    390 
    391          if (result) {
    392            if (!buf.append(valstr)) {
    393              return false;
    394            }
    395            return true;
    396          }
    397        }
    398      }
    399 
    400      {
    401        // When falling back try to generate a better string
    402        // representation by skipping the prelude, and also removing
    403        // the enclosing parentheses.
    404        bool success;
    405        JS::AutoCheckCannotGC nogc;
    406        if (valstr->hasLatin1Chars()) {
    407          success = ArgsAndBodySubstring(valstr->latin1Range(nogc), &voffset,
    408                                         &vlength);
    409        } else {
    410          success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset,
    411                                         &vlength);
    412        }
    413        if (!success) {
    414          kind = PropertyKind::Normal;
    415        }
    416      }
    417 
    418      if (kind == PropertyKind::Getter) {
    419        if (!buf.append("get ")) {
    420          return false;
    421        }
    422      } else if (kind == PropertyKind::Setter) {
    423        if (!buf.append("set ")) {
    424          return false;
    425        }
    426      } else if (kind == PropertyKind::Method && fun) {
    427        if (fun->isAsync()) {
    428          if (!buf.append("async ")) {
    429            return false;
    430          }
    431        }
    432 
    433        if (fun->isGenerator()) {
    434          if (!buf.append('*')) {
    435            return false;
    436          }
    437        }
    438      }
    439    }
    440 
    441    bool needsBracket = id.isSymbol();
    442    if (needsBracket && !buf.append('[')) {
    443      return false;
    444    }
    445    if (!buf.append(idstr)) {
    446      return false;
    447    }
    448    if (needsBracket && !buf.append(']')) {
    449      return false;
    450    }
    451 
    452    if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
    453        kind == PropertyKind::Method) {
    454      if (!buf.appendSubstring(valstr, voffset, vlength)) {
    455        return false;
    456      }
    457    } else {
    458      if (!buf.append(':')) {
    459        return false;
    460      }
    461      if (!buf.append(valstr)) {
    462        return false;
    463      }
    464    }
    465    return true;
    466  };
    467 
    468  RootedId id(cx);
    469  Rooted<Maybe<PropertyDescriptor>> desc(cx);
    470  RootedValue val(cx);
    471  for (size_t i = 0; i < idv.length(); ++i) {
    472    id = idv[i];
    473    if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
    474      return nullptr;
    475    }
    476 
    477    if (desc.isNothing()) {
    478      continue;
    479    }
    480 
    481    if (desc->isAccessorDescriptor()) {
    482      if (desc->hasGetter() && desc->getter()) {
    483        val.setObject(*desc->getter());
    484        if (!AddProperty(id, val, PropertyKind::Getter)) {
    485          return nullptr;
    486        }
    487      }
    488      if (desc->hasSetter() && desc->setter()) {
    489        val.setObject(*desc->setter());
    490        if (!AddProperty(id, val, PropertyKind::Setter)) {
    491          return nullptr;
    492        }
    493      }
    494      continue;
    495    }
    496 
    497    val.set(desc->value());
    498 
    499    JSFunction* fun = nullptr;
    500    if (IsFunctionObject(val, &fun) && fun->isMethod()) {
    501      if (!AddProperty(id, val, PropertyKind::Method)) {
    502        return nullptr;
    503      }
    504      continue;
    505    }
    506 
    507    if (!AddProperty(id, val, PropertyKind::Normal)) {
    508      return nullptr;
    509    }
    510  }
    511 
    512  if (!buf.append('}')) {
    513    return nullptr;
    514  }
    515  if (outermost && !buf.append(')')) {
    516    return nullptr;
    517  }
    518 
    519  return buf.finishString();
    520 }
    521 
    522 static JSString* GetBuiltinTagSlow(JSContext* cx, HandleObject obj) {
    523  // Step 4.
    524  bool isArray;
    525  if (!IsArray(cx, obj, &isArray)) {
    526    return nullptr;
    527  }
    528 
    529  // Step 5.
    530  if (isArray) {
    531    return cx->names().object_Array_;
    532  }
    533 
    534  // Steps 6-14.
    535  ESClass cls;
    536  if (!JS::GetBuiltinClass(cx, obj, &cls)) {
    537    return nullptr;
    538  }
    539 
    540  switch (cls) {
    541    case ESClass::String:
    542      return cx->names().object_String_;
    543    case ESClass::Arguments:
    544      return cx->names().object_Arguments_;
    545    case ESClass::Error:
    546      return cx->names().object_Error_;
    547    case ESClass::Boolean:
    548      return cx->names().object_Boolean_;
    549    case ESClass::Number:
    550      return cx->names().object_Number_;
    551    case ESClass::Date:
    552      return cx->names().object_Date_;
    553    case ESClass::RegExp:
    554      return cx->names().object_RegExp_;
    555    default:
    556      if (obj->isCallable()) {
    557        return cx->names().object_Function_;
    558      }
    559      return cx->names().object_Object_;
    560  }
    561 }
    562 
    563 static MOZ_ALWAYS_INLINE JSString* GetBuiltinTagFast(JSObject* obj,
    564                                                     JSContext* cx) {
    565  const JSClass* clasp = obj->getClass();
    566  MOZ_ASSERT(!clasp->isProxyObject());
    567 
    568  // Optimize the non-proxy case to bypass GetBuiltinClass.
    569  if (clasp == &PlainObject::class_) {
    570    // This case is by far the most common so we handle it first.
    571    return cx->names().object_Object_;
    572  }
    573 
    574  if (clasp == &ArrayObject::class_) {
    575    return cx->names().object_Array_;
    576  }
    577 
    578  if (clasp->isJSFunction()) {
    579    return cx->names().object_Function_;
    580  }
    581 
    582  if (clasp == &StringObject::class_) {
    583    return cx->names().object_String_;
    584  }
    585 
    586  if (clasp == &NumberObject::class_) {
    587    return cx->names().object_Number_;
    588  }
    589 
    590  if (clasp == &BooleanObject::class_) {
    591    return cx->names().object_Boolean_;
    592  }
    593 
    594  if (clasp == &DateObject::class_) {
    595    return cx->names().object_Date_;
    596  }
    597 
    598  if (clasp == &RegExpObject::class_) {
    599    return cx->names().object_RegExp_;
    600  }
    601 
    602  if (obj->is<ArgumentsObject>()) {
    603    return cx->names().object_Arguments_;
    604  }
    605 
    606  if (obj->is<ErrorObject>()) {
    607    return cx->names().object_Error_;
    608  }
    609 
    610  if (obj->isCallable()) {
    611    return cx->names().object_Function_;
    612  }
    613 
    614  return cx->names().object_Object_;
    615 }
    616 
    617 // For primitive values we try to avoid allocating the object if we can
    618 // determine that the prototype it would use does not define Symbol.toStringTag.
    619 static JSAtom* MaybeObjectToStringPrimitive(JSContext* cx, const Value& v) {
    620  JSProtoKey protoKey = js::PrimitiveToProtoKey(cx, v);
    621 
    622  // If prototype doesn't exist yet, just fall through.
    623  JSObject* proto = cx->global()->maybeGetPrototype(protoKey);
    624  if (!proto) {
    625    return nullptr;
    626  }
    627 
    628  // If determining this may have side-effects, we must instead create the
    629  // object normally since it is the receiver while looking up
    630  // Symbol.toStringTag.
    631  if (MaybeHasInterestingSymbolProperty(
    632          cx, proto, cx->wellKnownSymbols().toStringTag, nullptr)) {
    633    return nullptr;
    634  }
    635 
    636  // Return the direct result.
    637  switch (protoKey) {
    638    case JSProto_String:
    639      return cx->names().object_String_;
    640    case JSProto_Number:
    641      return cx->names().object_Number_;
    642    case JSProto_Boolean:
    643      return cx->names().object_Boolean_;
    644    case JSProto_Symbol:
    645      return cx->names().object_Symbol_;
    646    case JSProto_BigInt:
    647      return cx->names().object_BigInt_;
    648    default:
    649      break;
    650  }
    651 
    652  return nullptr;
    653 }
    654 
    655 // ES6 19.1.3.6
    656 bool js::obj_toString(JSContext* cx, unsigned argc, Value* vp) {
    657  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object.prototype", "toString");
    658  CallArgs args = CallArgsFromVp(argc, vp);
    659  RootedObject obj(cx);
    660 
    661  if (args.thisv().isPrimitive()) {
    662    // Step 1.
    663    if (args.thisv().isUndefined()) {
    664      args.rval().setString(cx->names().object_Undefined_);
    665      return true;
    666    }
    667 
    668    // Step 2.
    669    if (args.thisv().isNull()) {
    670      args.rval().setString(cx->names().object_Null_);
    671      return true;
    672    }
    673 
    674    // Try fast-path for primitives. This is unusual but we encounter code like
    675    // this in the wild.
    676    JSAtom* result = MaybeObjectToStringPrimitive(cx, args.thisv());
    677    if (result) {
    678      args.rval().setString(result);
    679      return true;
    680    }
    681 
    682    // Step 3.
    683    obj = ToObject(cx, args.thisv());
    684    if (!obj) {
    685      return false;
    686    }
    687  } else {
    688    obj = &args.thisv().toObject();
    689  }
    690 
    691  // When |obj| is a non-proxy object, compute |builtinTag| only when needed.
    692  RootedString builtinTag(cx);
    693  if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
    694    builtinTag = GetBuiltinTagSlow(cx, obj);
    695    if (!builtinTag) {
    696      return false;
    697    }
    698  }
    699 
    700  // Step 15.
    701  RootedValue tag(cx);
    702  if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toStringTag,
    703                                    &tag)) {
    704    return false;
    705  }
    706 
    707  // Step 16.
    708  if (!tag.isString()) {
    709    if (!builtinTag) {
    710      builtinTag = GetBuiltinTagFast(obj, cx);
    711 #ifdef DEBUG
    712      // Assert this fast path is correct and matches BuiltinTagSlow.
    713      JSString* builtinTagSlow = GetBuiltinTagSlow(cx, obj);
    714      if (!builtinTagSlow) {
    715        return false;
    716      }
    717      MOZ_ASSERT(builtinTagSlow == builtinTag);
    718 #endif
    719    }
    720 
    721    args.rval().setString(builtinTag);
    722    return true;
    723  }
    724 
    725  // Step 17.
    726  StringBuilder sb(cx);
    727  if (!sb.append("[object ") || !sb.append(tag.toString()) || !sb.append(']')) {
    728    return false;
    729  }
    730 
    731  JSString* str = sb.finishAtom();
    732  if (!str) {
    733    return false;
    734  }
    735 
    736  args.rval().setString(str);
    737  return true;
    738 }
    739 
    740 JSString* js::ObjectClassToString(JSContext* cx, JSObject* obj) {
    741  AutoUnsafeCallWithABI unsafe;
    742 
    743  if (MaybeHasInterestingSymbolProperty(cx, obj,
    744                                        cx->wellKnownSymbols().toStringTag)) {
    745    return nullptr;
    746  }
    747  return GetBuiltinTagFast(obj, cx);
    748 }
    749 
    750 static bool obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
    751  CallArgs args = CallArgsFromVp(argc, vp);
    752 
    753  if (!args.requireAtLeast(cx, "Object.setPrototypeOf", 2)) {
    754    return false;
    755  }
    756 
    757  /* Step 1-2. */
    758  if (args[0].isNullOrUndefined()) {
    759    JS_ReportErrorNumberASCII(
    760        cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
    761        args[0].isNull() ? "null" : "undefined", "object");
    762    return false;
    763  }
    764 
    765  /* Step 3. */
    766  if (!args[1].isObjectOrNull()) {
    767    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    768                              JSMSG_NOT_EXPECTED_TYPE, "Object.setPrototypeOf",
    769                              "an object or null",
    770                              InformalValueTypeName(args[1]));
    771    return false;
    772  }
    773 
    774  /* Step 4. */
    775  if (!args[0].isObject()) {
    776    args.rval().set(args[0]);
    777    return true;
    778  }
    779 
    780  /* Step 5-7. */
    781  RootedObject obj(cx, &args[0].toObject());
    782  RootedObject newProto(cx, args[1].toObjectOrNull());
    783  if (!SetPrototype(cx, obj, newProto)) {
    784    return false;
    785  }
    786 
    787  /* Step 8. */
    788  args.rval().set(args[0]);
    789  return true;
    790 }
    791 
    792 static bool PropertyIsEnumerable(JSContext* cx, HandleObject obj, HandleId id,
    793                                 bool* enumerable) {
    794  PropertyResult prop;
    795  if (obj->is<NativeObject>() &&
    796      NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop)) {
    797    if (prop.isNotFound()) {
    798      *enumerable = false;
    799      return true;
    800    }
    801 
    802    JS::PropertyAttributes attrs = GetPropertyAttributes(obj, prop);
    803    *enumerable = attrs.enumerable();
    804    return true;
    805  }
    806 
    807  Rooted<Maybe<PropertyDescriptor>> desc(cx);
    808  if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
    809    return false;
    810  }
    811 
    812  *enumerable = desc.isSome() && desc->enumerable();
    813  return true;
    814 }
    815 
    816 // Returns true if properties not named "__proto__" can be added to |obj|
    817 // with a fast path that doesn't check any properties on the prototype chain.
    818 static bool CanAddNewPropertyExcludingProtoFast(PlainObject* obj) {
    819  if (!obj->isExtensible() || obj->isUsedAsPrototype()) {
    820    return false;
    821  }
    822 
    823  // Don't fastpath assign if we're watching for property value changes.
    824  if (Watchtower::watchesPropertyValueChange(obj)) {
    825    return false;
    826  }
    827 
    828  // Ensure the object has no non-writable properties or getters/setters.
    829  // For now only support PlainObjects so that we don't have to worry about
    830  // resolve hooks and other JSClass hooks.
    831  while (true) {
    832    if (obj->hasNonWritableOrAccessorPropExclProto()) {
    833      return false;
    834    }
    835 
    836    JSObject* proto = obj->staticPrototype();
    837    if (!proto) {
    838      return true;
    839    }
    840    if (!proto->is<PlainObject>()) {
    841      return false;
    842    }
    843    obj = &proto->as<PlainObject>();
    844  }
    845 }
    846 
    847 #ifdef DEBUG
    848 void PlainObjectAssignCache::assertValid() const {
    849  MOZ_ASSERT(emptyToShape_);
    850  MOZ_ASSERT(fromShape_);
    851  MOZ_ASSERT(newToShape_);
    852 
    853  MOZ_ASSERT(emptyToShape_->propMapLength() == 0);
    854  MOZ_ASSERT(emptyToShape_->base() == newToShape_->base());
    855  MOZ_ASSERT(emptyToShape_->numFixedSlots() == newToShape_->numFixedSlots());
    856 
    857  MOZ_ASSERT(emptyToShape_->getObjectClass() == &PlainObject::class_);
    858  MOZ_ASSERT(fromShape_->getObjectClass() == &PlainObject::class_);
    859 
    860  MOZ_ASSERT(fromShape_->slotSpan() == newToShape_->slotSpan());
    861 }
    862 #endif
    863 
    864 [[nodiscard]] static bool TryAssignPlain(JSContext* cx, HandleObject to,
    865                                         HandleObject from, bool* optimized) {
    866  // Object.assign is used with PlainObjects most of the time. This is a fast
    867  // path to optimize that case. This lets us avoid checks that are only
    868  // relevant for other JSClasses.
    869 
    870  MOZ_ASSERT(*optimized == false);
    871 
    872  if (!from->is<PlainObject>() || !to->is<PlainObject>()) {
    873    return true;
    874  }
    875 
    876  // Don't use the fast path if |from| may have extra indexed properties.
    877  Handle<PlainObject*> fromPlain = from.as<PlainObject>();
    878  if (fromPlain->getDenseInitializedLength() > 0 || fromPlain->isIndexed()) {
    879    return true;
    880  }
    881  MOZ_ASSERT(!fromPlain->getClass()->getNewEnumerate());
    882  MOZ_ASSERT(!fromPlain->getClass()->getEnumerate());
    883 
    884  // Empty |from| objects are common, so check for this first.
    885  if (fromPlain->empty()) {
    886    *optimized = true;
    887    return true;
    888  }
    889 
    890  Handle<PlainObject*> toPlain = to.as<PlainObject>();
    891  if (!CanAddNewPropertyExcludingProtoFast(toPlain)) {
    892    return true;
    893  }
    894 
    895  const bool toWasEmpty = toPlain->empty();
    896  if (toWasEmpty) {
    897    const PlainObjectAssignCache& cache = cx->realm()->plainObjectAssignCache;
    898    SharedShape* newShape = cache.lookup(toPlain->shape(), fromPlain->shape());
    899    if (newShape) {
    900      *optimized = true;
    901      uint32_t oldSpan = 0;
    902      uint32_t newSpan = newShape->slotSpan();
    903      if (!toPlain->setShapeAndAddNewSlots(cx, newShape, oldSpan, newSpan)) {
    904        return false;
    905      }
    906      MOZ_ASSERT(fromPlain->slotSpan() == newSpan);
    907      for (size_t i = 0; i < newSpan; i++) {
    908        toPlain->initSlot(i, fromPlain->getSlot(i));
    909      }
    910      return true;
    911    }
    912  }
    913 
    914  // Get a list of all enumerable |from| properties.
    915 
    916  Rooted<PropertyInfoWithKeyVector> props(cx, PropertyInfoWithKeyVector(cx));
    917 
    918 #ifdef DEBUG
    919  Rooted<Shape*> fromShape(cx, fromPlain->shape());
    920 #endif
    921 
    922  bool hasPropsWithNonDefaultAttrs = false;
    923  bool hasOnlyEnumerableProps = true;
    924  for (ShapePropertyIter<NoGC> iter(fromPlain->shape()); !iter.done(); iter++) {
    925    // Symbol properties need to be assigned last. For now fall back to the
    926    // slow path if we see a symbol property.
    927    jsid id = iter->key();
    928    if (MOZ_UNLIKELY(id.isSymbol())) {
    929      return true;
    930    }
    931    // __proto__ is not supported by CanAddNewPropertyExcludingProtoFast.
    932    if (MOZ_UNLIKELY(id.isAtom(cx->names().proto_))) {
    933      return true;
    934    }
    935    if (MOZ_UNLIKELY(!iter->isDataProperty())) {
    936      return true;
    937    }
    938    if (iter->flags() != PropertyFlags::defaultDataPropFlags) {
    939      hasPropsWithNonDefaultAttrs = true;
    940      if (!iter->enumerable()) {
    941        hasOnlyEnumerableProps = false;
    942        continue;
    943      }
    944    }
    945    if (MOZ_UNLIKELY(!props.append(*iter))) {
    946      return false;
    947    }
    948  }
    949 
    950  MOZ_ASSERT_IF(hasOnlyEnumerableProps && !fromPlain->inDictionaryMode(),
    951                fromPlain->slotSpan() == props.length());
    952 
    953  *optimized = true;
    954 
    955  Rooted<Shape*> origToShape(cx, toPlain->shape());
    956 
    957  // If the |to| object has no properties and the |from| object only has plain
    958  // enumerable/writable/configurable data properties, try to use its shape or
    959  // property map.
    960  if (toWasEmpty && !hasPropsWithNonDefaultAttrs) {
    961    CanReuseShape canReuse =
    962        toPlain->canReuseShapeForNewProperties(fromPlain->shape());
    963    if (canReuse != CanReuseShape::NoReuse) {
    964      SharedShape* newShape;
    965      if (canReuse == CanReuseShape::CanReuseShape) {
    966        newShape = fromPlain->sharedShape();
    967      } else {
    968        // Get a shape with fromPlain's PropMap and ObjectFlags (because we need
    969        // the HasEnumerable flag checked in canReuseShapeForNewProperties) and
    970        // the other fields (BaseShape, numFixedSlots) unchanged.
    971        MOZ_ASSERT(canReuse == CanReuseShape::CanReusePropMap);
    972        ObjectFlags objectFlags = fromPlain->sharedShape()->objectFlags();
    973        Rooted<SharedPropMap*> map(cx, fromPlain->sharedShape()->propMap());
    974        uint32_t mapLength = fromPlain->sharedShape()->propMapLength();
    975        BaseShape* base = toPlain->sharedShape()->base();
    976        uint32_t nfixed = toPlain->sharedShape()->numFixedSlots();
    977        newShape = SharedShape::getPropMapShape(cx, base, nfixed, map,
    978                                                mapLength, objectFlags);
    979        if (!newShape) {
    980          return false;
    981        }
    982      }
    983      uint32_t oldSpan = 0;
    984      uint32_t newSpan = props.length();
    985      if (!toPlain->setShapeAndAddNewSlots(cx, newShape, oldSpan, newSpan)) {
    986        return false;
    987      }
    988      MOZ_ASSERT(fromPlain->slotSpan() == newSpan);
    989      MOZ_ASSERT(toPlain->slotSpan() == newSpan);
    990      for (size_t i = 0; i < newSpan; i++) {
    991        toPlain->initSlot(i, fromPlain->getSlot(i));
    992      }
    993      PlainObjectAssignCache& cache = cx->realm()->plainObjectAssignCache;
    994      cache.fill(&origToShape->asShared(), fromPlain->sharedShape(), newShape);
    995      return true;
    996    }
    997  }
    998 
    999  RootedValue propValue(cx);
   1000  RootedId nextKey(cx);
   1001 
   1002  for (size_t i = props.length(); i > 0; i--) {
   1003    // Assert |from| still has the same properties.
   1004    MOZ_ASSERT(fromPlain->shape() == fromShape);
   1005 
   1006    PropertyInfoWithKey fromProp = props[i - 1];
   1007    MOZ_ASSERT(fromProp.isDataProperty());
   1008    MOZ_ASSERT(fromProp.enumerable());
   1009 
   1010    nextKey = fromProp.key();
   1011    propValue = fromPlain->getSlot(fromProp.slot());
   1012 
   1013    if (!toWasEmpty) {
   1014      if (Maybe<PropertyInfo> toProp = toPlain->lookup(cx, nextKey)) {
   1015        MOZ_ASSERT(toProp->isDataProperty());
   1016        MOZ_ASSERT(toProp->writable());
   1017        toPlain->setSlot(toProp->slot(), propValue);
   1018        continue;
   1019      }
   1020    }
   1021 
   1022    MOZ_ASSERT(!toPlain->containsPure(nextKey));
   1023 
   1024    if (!AddDataPropertyToPlainObject(cx, toPlain, nextKey, propValue)) {
   1025      return false;
   1026    }
   1027  }
   1028 
   1029  // Note: dictionary shapes are not supported by the cache because they have a
   1030  // more complicated slot layout (the slot numbers may not match the property
   1031  // definition order and the slots may contain holes).
   1032  if (toWasEmpty && hasOnlyEnumerableProps && !fromPlain->inDictionaryMode() &&
   1033      !toPlain->inDictionaryMode()) {
   1034    PlainObjectAssignCache& cache = cx->realm()->plainObjectAssignCache;
   1035    cache.fill(&origToShape->asShared(), fromPlain->sharedShape(),
   1036               toPlain->sharedShape());
   1037  }
   1038 
   1039  return true;
   1040 }
   1041 
   1042 static bool TryAssignNative(JSContext* cx, HandleObject to, HandleObject from,
   1043                            bool* optimized) {
   1044  MOZ_ASSERT(*optimized == false);
   1045 
   1046  if (!from->is<NativeObject>() || !to->is<NativeObject>()) {
   1047    return true;
   1048  }
   1049 
   1050  // Don't use the fast path if |from| may have extra indexed or lazy
   1051  // properties.
   1052  NativeObject* fromNative = &from->as<NativeObject>();
   1053  if (fromNative->getDenseInitializedLength() > 0 || fromNative->isIndexed() ||
   1054      fromNative->is<TypedArrayObject>() ||
   1055      fromNative->getClass()->getNewEnumerate() ||
   1056      fromNative->getClass()->getEnumerate()) {
   1057    return true;
   1058  }
   1059 
   1060  // Get a list of |from| properties. As long as from->shape() == fromShape
   1061  // we can use this to speed up both the enumerability check and the GetProp.
   1062 
   1063  Rooted<PropertyInfoWithKeyVector> props(cx, PropertyInfoWithKeyVector(cx));
   1064 
   1065  Rooted<NativeShape*> fromShape(cx, fromNative->shape());
   1066  for (ShapePropertyIter<NoGC> iter(fromShape); !iter.done(); iter++) {
   1067    // Symbol properties need to be assigned last. For now fall back to the
   1068    // slow path if we see a symbol property.
   1069    if (MOZ_UNLIKELY(iter->key().isSymbol())) {
   1070      return true;
   1071    }
   1072    if (MOZ_UNLIKELY(!props.append(*iter))) {
   1073      return false;
   1074    }
   1075  }
   1076 
   1077  *optimized = true;
   1078 
   1079  RootedValue propValue(cx);
   1080  RootedId nextKey(cx);
   1081  RootedValue toReceiver(cx, ObjectValue(*to));
   1082 
   1083  for (size_t i = props.length(); i > 0; i--) {
   1084    PropertyInfoWithKey prop = props[i - 1];
   1085    nextKey = prop.key();
   1086 
   1087    // If |from| still has the same shape, it must still be a NativeObject with
   1088    // the properties in |props|.
   1089    if (MOZ_LIKELY(from->shape() == fromShape && prop.isDataProperty())) {
   1090      if (!prop.enumerable()) {
   1091        continue;
   1092      }
   1093      propValue = from->as<NativeObject>().getSlot(prop.slot());
   1094    } else {
   1095      // |from| changed shape or the property is not a data property, so
   1096      // we have to do the slower enumerability check and GetProp.
   1097      bool enumerable;
   1098      if (!PropertyIsEnumerable(cx, from, nextKey, &enumerable)) {
   1099        return false;
   1100      }
   1101      if (!enumerable) {
   1102        continue;
   1103      }
   1104      if (!GetProperty(cx, from, from, nextKey, &propValue)) {
   1105        return false;
   1106      }
   1107    }
   1108 
   1109    ObjectOpResult result;
   1110    if (MOZ_UNLIKELY(
   1111            !SetProperty(cx, to, nextKey, propValue, toReceiver, result))) {
   1112      return false;
   1113    }
   1114    if (MOZ_UNLIKELY(!result.checkStrict(cx, to, nextKey))) {
   1115      return false;
   1116    }
   1117  }
   1118 
   1119  return true;
   1120 }
   1121 
   1122 static bool AssignSlow(JSContext* cx, HandleObject to, HandleObject from) {
   1123  // Step 4.b.ii.
   1124  RootedIdVector keys(cx);
   1125  if (!GetPropertyKeys(
   1126          cx, from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &keys)) {
   1127    return false;
   1128  }
   1129 
   1130  // Step 4.c.
   1131  RootedId nextKey(cx);
   1132  RootedValue propValue(cx);
   1133  for (size_t i = 0, len = keys.length(); i < len; i++) {
   1134    nextKey = keys[i];
   1135 
   1136    // Step 4.c.i.
   1137    bool enumerable;
   1138    if (MOZ_UNLIKELY(!PropertyIsEnumerable(cx, from, nextKey, &enumerable))) {
   1139      return false;
   1140    }
   1141    if (!enumerable) {
   1142      continue;
   1143    }
   1144 
   1145    // Step 4.c.ii.1.
   1146    if (MOZ_UNLIKELY(!GetProperty(cx, from, from, nextKey, &propValue))) {
   1147      return false;
   1148    }
   1149 
   1150    // Step 4.c.ii.2.
   1151    if (MOZ_UNLIKELY(!SetProperty(cx, to, nextKey, propValue))) {
   1152      return false;
   1153    }
   1154  }
   1155 
   1156  return true;
   1157 }
   1158 
   1159 JS_PUBLIC_API bool JS_AssignObject(JSContext* cx, JS::HandleObject target,
   1160                                   JS::HandleObject src) {
   1161  bool optimized = false;
   1162 
   1163  if (!TryAssignPlain(cx, target, src, &optimized)) {
   1164    return false;
   1165  }
   1166  if (optimized) {
   1167    return true;
   1168  }
   1169 
   1170  if (!TryAssignNative(cx, target, src, &optimized)) {
   1171    return false;
   1172  }
   1173  if (optimized) {
   1174    return true;
   1175  }
   1176 
   1177  return AssignSlow(cx, target, src);
   1178 }
   1179 
   1180 // ES2018 draft rev 48ad2688d8f964da3ea8c11163ef20eb126fb8a4
   1181 // 19.1.2.1 Object.assign(target, ...sources)
   1182 static bool obj_assign(JSContext* cx, unsigned argc, Value* vp) {
   1183  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object", "assign");
   1184  CallArgs args = CallArgsFromVp(argc, vp);
   1185  RootedTuple<JSObject*, JSObject*> roots(cx);
   1186 
   1187  // Step 1.
   1188  RootedField<JSObject*, 0> to(roots, ToObject(cx, args.get(0)));
   1189  if (!to) {
   1190    return false;
   1191  }
   1192 
   1193  // Note: step 2 is implicit. If there are 0 arguments, ToObject throws. If
   1194  // there's 1 argument, the loop below is a no-op.
   1195 
   1196  // Step 4.
   1197  for (size_t i = 1; i < args.length(); i++) {
   1198    // Step 4.a.
   1199    if (args[i].isNullOrUndefined()) {
   1200      continue;
   1201    }
   1202 
   1203    // Step 4.b.i.
   1204    RootedField<JSObject*, 1> from(roots, ToObject(cx, args[i]));
   1205    if (!from) {
   1206      return false;
   1207    }
   1208 
   1209    // Steps 4.b.ii, 4.c.
   1210    if (!JS_AssignObject(cx, to, from)) {
   1211      return false;
   1212    }
   1213  }
   1214 
   1215  // Step 5.
   1216  args.rval().setObject(*to);
   1217  return true;
   1218 }
   1219 
   1220 /* ES5 15.2.4.6. */
   1221 bool js::obj_isPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
   1222  CallArgs args = CallArgsFromVp(argc, vp);
   1223 
   1224  /* Step 1. */
   1225  if (args.length() < 1 || !args[0].isObject()) {
   1226    args.rval().setBoolean(false);
   1227    return true;
   1228  }
   1229 
   1230  /* Step 2. */
   1231  RootedObject obj(cx, ToObject(cx, args.thisv()));
   1232  if (!obj) {
   1233    return false;
   1234  }
   1235 
   1236  /* Step 3. */
   1237  bool isPrototype;
   1238  if (!IsPrototypeOf(cx, obj, &args[0].toObject(), &isPrototype)) {
   1239    return false;
   1240  }
   1241  args.rval().setBoolean(isPrototype);
   1242  return true;
   1243 }
   1244 
   1245 PlainObject* js::ObjectCreateImpl(JSContext* cx, HandleObject proto,
   1246                                  NewObjectKind newKind) {
   1247  // Give the new object a small number of fixed slots, like we do for empty
   1248  // object literals ({}).
   1249  gc::AllocKind allocKind = NewObjectGCKind();
   1250  return NewPlainObjectWithProtoAndAllocKind(cx, proto, allocKind, newKind);
   1251 }
   1252 
   1253 PlainObject* js::ObjectCreateWithTemplate(JSContext* cx,
   1254                                          Handle<PlainObject*> templateObj) {
   1255  RootedObject proto(cx, templateObj->staticPrototype());
   1256  return ObjectCreateImpl(cx, proto, GenericObject);
   1257 }
   1258 
   1259 // ES 2017 draft 19.1.2.3.1
   1260 static bool ObjectDefineProperties(JSContext* cx, HandleObject obj,
   1261                                   HandleValue properties,
   1262                                   bool* failedOnWindowProxy) {
   1263  // Step 1. implicit
   1264  // Step 2.
   1265  RootedObject props(cx, ToObject(cx, properties));
   1266  if (!props) {
   1267    return false;
   1268  }
   1269 
   1270  // Step 3.
   1271  RootedIdVector keys(cx);
   1272  if (!GetPropertyKeys(
   1273          cx, props, JSITER_OWNONLY | JSITER_SYMBOLS | JSITER_HIDDEN, &keys)) {
   1274    return false;
   1275  }
   1276 
   1277  RootedId nextKey(cx);
   1278  Rooted<Maybe<PropertyDescriptor>> keyDesc(cx);
   1279  Rooted<PropertyDescriptor> desc(cx);
   1280  RootedValue descObj(cx);
   1281 
   1282  // Step 4.
   1283  Rooted<PropertyDescriptorVector> descriptors(cx,
   1284                                               PropertyDescriptorVector(cx));
   1285  RootedIdVector descriptorKeys(cx);
   1286 
   1287  // Step 5.
   1288  for (size_t i = 0, len = keys.length(); i < len; i++) {
   1289    nextKey = keys[i];
   1290 
   1291    // Step 5.a.
   1292    if (!GetOwnPropertyDescriptor(cx, props, nextKey, &keyDesc)) {
   1293      return false;
   1294    }
   1295 
   1296    // Step 5.b.
   1297    if (keyDesc.isSome() && keyDesc->enumerable()) {
   1298      if (!GetProperty(cx, props, props, nextKey, &descObj) ||
   1299          !ToPropertyDescriptor(cx, descObj, true, &desc) ||
   1300          !descriptors.append(desc) || !descriptorKeys.append(nextKey)) {
   1301        return false;
   1302      }
   1303    }
   1304  }
   1305 
   1306  // Step 6.
   1307  *failedOnWindowProxy = false;
   1308  for (size_t i = 0, len = descriptors.length(); i < len; i++) {
   1309    ObjectOpResult result;
   1310    if (!DefineProperty(cx, obj, descriptorKeys[i], descriptors[i], result)) {
   1311      return false;
   1312    }
   1313 
   1314    if (!result.ok()) {
   1315      if (result.failureCode() == JSMSG_CANT_DEFINE_WINDOW_NC) {
   1316        *failedOnWindowProxy = true;
   1317      } else if (!result.checkStrict(cx, obj, descriptorKeys[i])) {
   1318        return false;
   1319      }
   1320    }
   1321  }
   1322 
   1323  return true;
   1324 }
   1325 
   1326 // ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties])
   1327 bool js::obj_create(JSContext* cx, unsigned argc, Value* vp) {
   1328  CallArgs args = CallArgsFromVp(argc, vp);
   1329 
   1330  // Step 1.
   1331  if (!args.requireAtLeast(cx, "Object.create", 1)) {
   1332    return false;
   1333  }
   1334 
   1335  if (!args[0].isObjectOrNull()) {
   1336    UniqueChars bytes =
   1337        DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args[0], nullptr);
   1338    if (!bytes) {
   1339      return false;
   1340    }
   1341 
   1342    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1343                             JSMSG_UNEXPECTED_TYPE, bytes.get(),
   1344                             "not an object or null");
   1345    return false;
   1346  }
   1347 
   1348  // Step 2.
   1349  RootedObject proto(cx, args[0].toObjectOrNull());
   1350  Rooted<PlainObject*> obj(cx, ObjectCreateImpl(cx, proto));
   1351  if (!obj) {
   1352    return false;
   1353  }
   1354 
   1355  // Step 3.
   1356  if (args.hasDefined(1)) {
   1357    // we can't ever end up with failures to define on a WindowProxy
   1358    // here, because "obj" is never a WindowProxy.
   1359    bool failedOnWindowProxy = false;
   1360    if (!ObjectDefineProperties(cx, obj, args[1], &failedOnWindowProxy)) {
   1361      return false;
   1362    }
   1363    MOZ_ASSERT(!failedOnWindowProxy, "How did we get a WindowProxy here?");
   1364  }
   1365 
   1366  // Step 4.
   1367  args.rval().setObject(*obj);
   1368  return true;
   1369 }
   1370 
   1371 // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
   1372 // 6.2.4.4 FromPropertyDescriptor ( Desc )
   1373 static bool FromPropertyDescriptorToArray(
   1374    JSContext* cx, Handle<Maybe<PropertyDescriptor>> desc,
   1375    MutableHandleValue vp) {
   1376  // Step 1.
   1377  if (desc.isNothing()) {
   1378    vp.setUndefined();
   1379    return true;
   1380  }
   1381 
   1382  // Steps 2-11.
   1383  // Retrieve all property descriptor fields and place them into the result
   1384  // array. The actual return object is created in self-hosted code for
   1385  // performance reasons.
   1386 
   1387  int32_t attrsAndKind = 0;
   1388  if (desc->enumerable()) {
   1389    attrsAndKind |= ATTR_ENUMERABLE;
   1390  }
   1391  if (desc->configurable()) {
   1392    attrsAndKind |= ATTR_CONFIGURABLE;
   1393  }
   1394  if (!desc->isAccessorDescriptor()) {
   1395    if (desc->writable()) {
   1396      attrsAndKind |= ATTR_WRITABLE;
   1397    }
   1398    attrsAndKind |= DATA_DESCRIPTOR_KIND;
   1399  } else {
   1400    attrsAndKind |= ACCESSOR_DESCRIPTOR_KIND;
   1401  }
   1402 
   1403  Rooted<ArrayObject*> result(cx);
   1404  if (!desc->isAccessorDescriptor()) {
   1405    result = NewDenseFullyAllocatedArray(cx, 2);
   1406    if (!result) {
   1407      return false;
   1408    }
   1409    result->setDenseInitializedLength(2);
   1410 
   1411    result->initDenseElement(PROP_DESC_ATTRS_AND_KIND_INDEX,
   1412                             Int32Value(attrsAndKind));
   1413    result->initDenseElement(PROP_DESC_VALUE_INDEX, desc->value());
   1414  } else {
   1415    result = NewDenseFullyAllocatedArray(cx, 3);
   1416    if (!result) {
   1417      return false;
   1418    }
   1419    result->setDenseInitializedLength(3);
   1420 
   1421    result->initDenseElement(PROP_DESC_ATTRS_AND_KIND_INDEX,
   1422                             Int32Value(attrsAndKind));
   1423 
   1424    if (JSObject* get = desc->getter()) {
   1425      result->initDenseElement(PROP_DESC_GETTER_INDEX, ObjectValue(*get));
   1426    } else {
   1427      result->initDenseElement(PROP_DESC_GETTER_INDEX, UndefinedValue());
   1428    }
   1429 
   1430    if (JSObject* set = desc->setter()) {
   1431      result->initDenseElement(PROP_DESC_SETTER_INDEX, ObjectValue(*set));
   1432    } else {
   1433      result->initDenseElement(PROP_DESC_SETTER_INDEX, UndefinedValue());
   1434    }
   1435  }
   1436 
   1437  vp.setObject(*result);
   1438  return true;
   1439 }
   1440 
   1441 // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
   1442 // 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P )
   1443 bool js::GetOwnPropertyDescriptorToArray(JSContext* cx, unsigned argc,
   1444                                         Value* vp) {
   1445  CallArgs args = CallArgsFromVp(argc, vp);
   1446  MOZ_ASSERT(args.length() == 2);
   1447 
   1448  // Step 1.
   1449  RootedObject obj(cx, ToObject(cx, args[0]));
   1450  if (!obj) {
   1451    return false;
   1452  }
   1453 
   1454  // Step 2.
   1455  RootedId id(cx);
   1456  if (!ToPropertyKey(cx, args[1], &id)) {
   1457    return false;
   1458  }
   1459 
   1460  // Step 3.
   1461  Rooted<Maybe<PropertyDescriptor>> desc(cx);
   1462  if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
   1463    return false;
   1464  }
   1465 
   1466  // Step 4.
   1467  return FromPropertyDescriptorToArray(cx, desc, args.rval());
   1468 }
   1469 
   1470 static bool NewValuePair(JSContext* cx, HandleValue val1, HandleValue val2,
   1471                         MutableHandleValue rval,
   1472                         gc::Heap heap = gc::Heap::Default) {
   1473  NewObjectKind kind =
   1474      heap == gc::Heap::Tenured ? TenuredObject : GenericObject;
   1475  ArrayObject* array = NewDenseFullyAllocatedArray(cx, 2, kind);
   1476  if (!array) {
   1477    return false;
   1478  }
   1479 
   1480  array->setDenseInitializedLength(2);
   1481  array->initDenseElement(0, val1);
   1482  array->initDenseElement(1, val2);
   1483 
   1484  rval.setObject(*array);
   1485  return true;
   1486 }
   1487 
   1488 enum class EnumerableOwnPropertiesKind { Keys, Values, KeysAndValues, Names };
   1489 
   1490 static bool HasEnumerableStringNonDataProperties(NativeObject* obj) {
   1491  // We also check for enumerability and symbol properties, so uninteresting
   1492  // non-data properties like |array.length| don't let us fall into the slow
   1493  // path.
   1494  if (!obj->hasEnumerableProperty()) {
   1495    return false;
   1496  }
   1497  for (ShapePropertyIter<NoGC> iter(obj->shape()); !iter.done(); iter++) {
   1498    if (!iter->isDataProperty() && iter->enumerable() &&
   1499        !iter->key().isSymbol()) {
   1500      return true;
   1501    }
   1502  }
   1503  return false;
   1504 }
   1505 
   1506 template <EnumerableOwnPropertiesKind kind>
   1507 static bool TryEnumerableOwnPropertiesNative(JSContext* cx, HandleObject obj,
   1508                                             MutableHandleValue rval,
   1509                                             bool* optimized) {
   1510  *optimized = false;
   1511 
   1512  // Use the fast path if |obj| has neither extra indexed properties nor a
   1513  // newEnumerate hook. String objects need to be special-cased, because
   1514  // they're only marked as indexed after their enumerate hook ran. And
   1515  // because their enumerate hook is slowish, it's more performant to
   1516  // exclude them directly instead of executing the hook first.
   1517  if (!obj->is<NativeObject>() || obj->as<NativeObject>().isIndexed() ||
   1518      obj->getClass()->getNewEnumerate() || obj->is<StringObject>()) {
   1519    return true;
   1520  }
   1521 
   1522  Handle<NativeObject*> nobj = obj.as<NativeObject>();
   1523 
   1524  // Resolve lazy properties on |nobj|.
   1525  if (JSEnumerateOp enumerate = nobj->getClass()->getEnumerate()) {
   1526    if (!enumerate(cx, nobj)) {
   1527      return false;
   1528    }
   1529 
   1530    // Ensure no extra indexed properties were added through enumerate().
   1531    if (nobj->isIndexed()) {
   1532      return true;
   1533    }
   1534  }
   1535 
   1536  *optimized = true;
   1537 
   1538  RootedValueVector properties(cx);
   1539  RootedValue key(cx);
   1540  RootedValue value(cx);
   1541 
   1542  if (kind == EnumerableOwnPropertiesKind::Keys) {
   1543    // If possible, attempt to use the shape's iterator cache.
   1544    Rooted<PropertyIteratorObject*> piter(cx,
   1545                                          LookupInShapeIteratorCache(cx, nobj));
   1546    if (piter) {
   1547      do {
   1548        NativeIterator* ni = piter->getNativeIterator();
   1549 
   1550        // Guard against indexes.
   1551        if (ni->mayHavePrototypeProperties()) {
   1552          break;
   1553        }
   1554 
   1555        IteratorProperty* properties = ni->propertiesBegin();
   1556        JSObject* array = NewDenseCopiedArray(cx, ni->numKeys(), properties);
   1557        if (!array) {
   1558          return false;
   1559        }
   1560 
   1561        rval.setObject(*array);
   1562        return true;
   1563 
   1564      } while (false);
   1565    }
   1566  }
   1567 
   1568  // Switch to allocating in the tenured heap if necessary to avoid possible
   1569  // quadratic behaviour marking stack rooted |properties| vector.
   1570  AutoSelectGCHeap gcHeap(cx, 1);
   1571 
   1572  // We have ensured |nobj| contains no extra indexed properties, so the
   1573  // only indexed properties we need to handle here are dense and typed
   1574  // array elements.
   1575  //
   1576  // Pre-reserve to avoid reallocating the properties vector frequently.
   1577  if (nobj->getDenseInitializedLength() > 0 &&
   1578      !properties.reserve(nobj->getDenseInitializedLength())) {
   1579    return false;
   1580  }
   1581  for (uint32_t i = 0, len = nobj->getDenseInitializedLength(); i < len; i++) {
   1582    value.set(nobj->getDenseElement(i));
   1583    if (value.isMagic(JS_ELEMENTS_HOLE)) {
   1584      continue;
   1585    }
   1586 
   1587    JSString* str;
   1588    if (kind != EnumerableOwnPropertiesKind::Values) {
   1589      static_assert(
   1590          NativeObject::MAX_DENSE_ELEMENTS_COUNT <= PropertyKey::IntMax,
   1591          "dense elements don't exceed PropertyKey::IntMax");
   1592      str = Int32ToStringWithHeap<CanGC>(cx, i, gcHeap);
   1593      if (!str) {
   1594        return false;
   1595      }
   1596    }
   1597 
   1598    if (kind == EnumerableOwnPropertiesKind::Keys ||
   1599        kind == EnumerableOwnPropertiesKind::Names) {
   1600      value.setString(str);
   1601    } else if (kind == EnumerableOwnPropertiesKind::KeysAndValues) {
   1602      key.setString(str);
   1603      if (!NewValuePair(cx, key, value, &value, gcHeap)) {
   1604        return false;
   1605      }
   1606    }
   1607 
   1608    if (!properties.append(value)) {
   1609      return false;
   1610    }
   1611  }
   1612 
   1613  if (obj->is<TypedArrayObject>()) {
   1614    Handle<TypedArrayObject*> tobj = obj.as<TypedArrayObject>();
   1615    size_t len = tobj->length().valueOr(0);
   1616 
   1617    // Fail early if the typed array contains too many elements for a
   1618    // dense array, because we likely OOM anyway when trying to allocate
   1619    // more than 2GB for the properties vector. This also means we don't
   1620    // need to handle indices greater than MAX_INT32 in the loop below.
   1621    if (len > NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
   1622      ReportOversizedAllocation(cx, JSMSG_ALLOC_OVERFLOW);
   1623      return false;
   1624    }
   1625 
   1626    MOZ_ASSERT(properties.empty(), "typed arrays cannot have dense elements");
   1627    if (!properties.resize(len)) {
   1628      return false;
   1629    }
   1630 
   1631    for (uint32_t i = 0; i < len; i++) {
   1632      JSString* str;
   1633      if (kind != EnumerableOwnPropertiesKind::Values) {
   1634        static_assert(
   1635            NativeObject::MAX_DENSE_ELEMENTS_COUNT <= PropertyKey::IntMax,
   1636            "dense elements don't exceed PropertyKey::IntMax");
   1637        str = Int32ToStringWithHeap<CanGC>(cx, i, gcHeap);
   1638        if (!str) {
   1639          return false;
   1640        }
   1641      }
   1642 
   1643      if (kind == EnumerableOwnPropertiesKind::Keys ||
   1644          kind == EnumerableOwnPropertiesKind::Names) {
   1645        value.setString(str);
   1646      } else if (kind == EnumerableOwnPropertiesKind::Values) {
   1647        if (!tobj->getElement<CanGC>(cx, i, &value)) {
   1648          return false;
   1649        }
   1650      } else {
   1651        key.setString(str);
   1652        if (!tobj->getElement<CanGC>(cx, i, &value)) {
   1653          return false;
   1654        }
   1655        if (!NewValuePair(cx, key, value, &value, gcHeap)) {
   1656          return false;
   1657        }
   1658      }
   1659 
   1660      properties[i].set(value);
   1661    }
   1662  }
   1663 
   1664  // Up to this point no side-effects through accessor properties are
   1665  // possible which could have replaced |obj| with a non-native object.
   1666  MOZ_ASSERT(obj->is<NativeObject>());
   1667  MOZ_ASSERT(obj.as<NativeObject>() == nobj);
   1668 
   1669  size_t approximatePropertyCount =
   1670      nobj->shape()->propMap()
   1671          ? nobj->shape()->propMap()->approximateEntryCount()
   1672          : 0;
   1673  if (!properties.reserve(properties.length() + approximatePropertyCount)) {
   1674    return false;
   1675  }
   1676 
   1677  if (kind == EnumerableOwnPropertiesKind::Keys ||
   1678      kind == EnumerableOwnPropertiesKind::Names ||
   1679      !HasEnumerableStringNonDataProperties(nobj)) {
   1680    // If |kind == Values| or |kind == KeysAndValues|:
   1681    // All enumerable properties with string property keys are data
   1682    // properties. This allows us to collect the property values while
   1683    // iterating over the shape hierarchy without worrying over accessors
   1684    // modifying any state.
   1685 
   1686    constexpr bool onlyEnumerable = kind != EnumerableOwnPropertiesKind::Names;
   1687    if (!onlyEnumerable || nobj->hasEnumerableProperty()) {
   1688      size_t elements = properties.length();
   1689      constexpr AllowGC allowGC =
   1690          kind != EnumerableOwnPropertiesKind::KeysAndValues ? AllowGC::NoGC
   1691                                                             : AllowGC::CanGC;
   1692      mozilla::Maybe<ShapePropertyIter<allowGC>> m;
   1693      if constexpr (allowGC == AllowGC::NoGC) {
   1694        m.emplace(nobj->shape());
   1695      } else {
   1696        m.emplace(cx, nobj->shape());
   1697      }
   1698      for (auto& iter = m.ref(); !iter.done(); iter++) {
   1699        jsid id = iter->key();
   1700        if ((onlyEnumerable && !iter->enumerable()) || id.isSymbol()) {
   1701          continue;
   1702        }
   1703        MOZ_ASSERT(!id.isInt(), "Unexpected indexed property");
   1704        MOZ_ASSERT_IF(kind == EnumerableOwnPropertiesKind::Values ||
   1705                          kind == EnumerableOwnPropertiesKind::KeysAndValues,
   1706                      iter->isDataProperty());
   1707 
   1708        if constexpr (kind == EnumerableOwnPropertiesKind::Keys ||
   1709                      kind == EnumerableOwnPropertiesKind::Names) {
   1710          value.setString(id.toString());
   1711        } else if constexpr (kind == EnumerableOwnPropertiesKind::Values) {
   1712          value.set(nobj->getSlot(iter->slot()));
   1713        } else {
   1714          key.setString(id.toString());
   1715          value.set(nobj->getSlot(iter->slot()));
   1716          if (!NewValuePair(cx, key, value, &value, gcHeap)) {
   1717            return false;
   1718          }
   1719        }
   1720 
   1721        if (!properties.append(value)) {
   1722          return false;
   1723        }
   1724      }
   1725 
   1726      // The (non-indexed) properties were visited in reverse iteration order,
   1727      // call std::reverse() to ensure they appear in iteration order.
   1728      std::reverse(properties.begin() + elements, properties.end());
   1729    }
   1730  } else {
   1731    MOZ_ASSERT(kind == EnumerableOwnPropertiesKind::Values ||
   1732               kind == EnumerableOwnPropertiesKind::KeysAndValues);
   1733 
   1734    // Get a list of all |obj| properties. As long as obj->shape()
   1735    // is equal to |objShape|, we can use this to speed up both the
   1736    // enumerability check and GetProperty.
   1737    Rooted<PropertyInfoWithKeyVector> props(cx, PropertyInfoWithKeyVector(cx));
   1738 
   1739    // Collect all non-symbol properties.
   1740    Rooted<NativeShape*> objShape(cx, nobj->shape());
   1741    for (ShapePropertyIter<NoGC> iter(objShape); !iter.done(); iter++) {
   1742      if (iter->key().isSymbol()) {
   1743        continue;
   1744      }
   1745      MOZ_ASSERT(!iter->key().isInt(), "Unexpected indexed property");
   1746 
   1747      if (!props.append(*iter)) {
   1748        return false;
   1749      }
   1750    }
   1751 
   1752    RootedId id(cx);
   1753    for (size_t i = props.length(); i > 0; i--) {
   1754      PropertyInfoWithKey prop = props[i - 1];
   1755      id = prop.key();
   1756 
   1757      // If |obj| still has the same shape, it must still be a NativeObject with
   1758      // the properties in |props|.
   1759      if (obj->shape() == objShape && prop.isDataProperty()) {
   1760        if (!prop.enumerable()) {
   1761          continue;
   1762        }
   1763        value = obj->as<NativeObject>().getSlot(prop.slot());
   1764      } else {
   1765        // |obj| changed shape or the property is not a data property,
   1766        // so we have to do the slower enumerability check and
   1767        // GetProperty.
   1768        bool enumerable;
   1769        if (!PropertyIsEnumerable(cx, obj, id, &enumerable)) {
   1770          return false;
   1771        }
   1772        if (!enumerable) {
   1773          continue;
   1774        }
   1775        if (!GetProperty(cx, obj, obj, id, &value)) {
   1776          return false;
   1777        }
   1778      }
   1779 
   1780      if (kind == EnumerableOwnPropertiesKind::KeysAndValues) {
   1781        key.setString(id.toString());
   1782        if (!NewValuePair(cx, key, value, &value, gcHeap)) {
   1783          return false;
   1784        }
   1785      }
   1786 
   1787      if (!properties.append(value)) {
   1788        return false;
   1789      }
   1790    }
   1791  }
   1792 
   1793  JSObject* array =
   1794      NewDenseCopiedArray(cx, properties.length(), properties.begin());
   1795  if (!array) {
   1796    return false;
   1797  }
   1798 
   1799  rval.setObject(*array);
   1800  return true;
   1801 }
   1802 
   1803 // Optimization dedicated for `Object.keys(..).length` JS pattern. This function
   1804 // replicates TryEnumerableOwnPropertiesNative code, except that instead of
   1805 // generating an array we only return the length of the array that would have
   1806 // been generated.
   1807 //
   1808 // As opposed to TryEnumerableOwnPropertiesNative, this function only support
   1809 // EnumerableOwnPropertiesKind::Keys variant.
   1810 static bool CountEnumerableOwnPropertiesNative(JSContext* cx, HandleObject obj,
   1811                                               int32_t& rval, bool* optimized) {
   1812  *optimized = false;
   1813 
   1814  // Use the fast path if |obj| has neither extra indexed properties nor a
   1815  // newEnumerate hook. String objects need to be special-cased, because
   1816  // they're only marked as indexed after their enumerate hook ran. And
   1817  // because their enumerate hook is slowish, it's more performant to
   1818  // exclude them directly instead of executing the hook first.
   1819  if (!obj->is<NativeObject>() || obj->as<NativeObject>().isIndexed() ||
   1820      obj->getClass()->getNewEnumerate() || obj->is<StringObject>()) {
   1821    return true;
   1822  }
   1823 
   1824  Handle<NativeObject*> nobj = obj.as<NativeObject>();
   1825 
   1826  // Resolve lazy properties on |nobj|.
   1827  if (JSEnumerateOp enumerate = nobj->getClass()->getEnumerate()) {
   1828    if (!enumerate(cx, nobj)) {
   1829      return false;
   1830    }
   1831 
   1832    // Ensure no extra indexed properties were added through enumerate().
   1833    if (nobj->isIndexed()) {
   1834      return true;
   1835    }
   1836  }
   1837 
   1838  *optimized = true;
   1839 
   1840  int32_t num_properties = 0;
   1841 
   1842  // If possible, attempt to use the shape's iterator cache.
   1843  Rooted<PropertyIteratorObject*> piter(cx,
   1844                                        LookupInShapeIteratorCache(cx, nobj));
   1845  if (piter) {
   1846    NativeIterator* ni = piter->getNativeIterator();
   1847 
   1848    // Guard against indexes.
   1849    if (!ni->mayHavePrototypeProperties()) {
   1850      rval = ni->numKeys();
   1851      return true;
   1852    }
   1853  }
   1854 
   1855  for (uint32_t i = 0, len = nobj->getDenseInitializedLength(); i < len; i++) {
   1856    if (nobj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
   1857      continue;
   1858    }
   1859 
   1860    num_properties += 1;
   1861  }
   1862 
   1863  if (obj->is<TypedArrayObject>()) {
   1864    Handle<TypedArrayObject*> tobj = obj.as<TypedArrayObject>();
   1865    size_t len = tobj->length().valueOr(0);
   1866 
   1867    // Fail early if the typed array contains too many elements for a
   1868    // dense array, because we likely OOM anyway when trying to allocate
   1869    // more than 2GB for the properties vector. This also means we don't
   1870    // need to handle indices greater than MAX_INT32 in the loop below.
   1871    if (len > NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
   1872      ReportOversizedAllocation(cx, JSMSG_ALLOC_OVERFLOW);
   1873      return false;
   1874    }
   1875 
   1876    MOZ_ASSERT(num_properties == 0, "typed arrays cannot have dense elements");
   1877    num_properties = len;
   1878  }
   1879 
   1880  // All enumerable properties with string property keys are data
   1881  // properties. This allows us to collect the property values while
   1882  // iterating over the shape hierarchy without worrying over accessors
   1883  // modifying any state.
   1884 
   1885  if (nobj->hasEnumerableProperty()) {
   1886    for (ShapePropertyIter<AllowGC::NoGC> iter(obj.as<NativeObject>()->shape());
   1887         !iter.done(); iter++) {
   1888      jsid id = iter->key();
   1889      if (!iter->enumerable() || id.isSymbol()) {
   1890        continue;
   1891      }
   1892      MOZ_ASSERT(!id.isInt(), "Unexpected indexed property");
   1893      num_properties += 1;
   1894    }
   1895  }
   1896 
   1897  rval = num_properties;
   1898  return true;
   1899 }
   1900 
   1901 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
   1902 // 7.3.21 EnumerableOwnProperties ( O, kind )
   1903 template <EnumerableOwnPropertiesKind kind>
   1904 static bool EnumerableOwnProperties(JSContext* cx, const JS::CallArgs& args) {
   1905  static_assert(kind == EnumerableOwnPropertiesKind::Values ||
   1906                    kind == EnumerableOwnPropertiesKind::KeysAndValues,
   1907                "Only implemented for Object.keys and Object.entries");
   1908 
   1909  // Step 1. (Step 1 of Object.{keys,values,entries}, really.)
   1910  RootedObject obj(cx, ToObject(cx, args.get(0)));
   1911  if (!obj) {
   1912    return false;
   1913  }
   1914 
   1915  bool optimized;
   1916  if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
   1917                                              &optimized)) {
   1918    return false;
   1919  }
   1920  if (optimized) {
   1921    return true;
   1922  }
   1923 
   1924  // Typed arrays are always handled in the fast path.
   1925  MOZ_ASSERT(!obj->is<TypedArrayObject>());
   1926 
   1927  // Step 2.
   1928  RootedIdVector ids(cx);
   1929  if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &ids)) {
   1930    return false;
   1931  }
   1932 
   1933  // Step 3.
   1934  RootedValueVector properties(cx);
   1935  size_t len = ids.length();
   1936  if (!properties.resize(len)) {
   1937    return false;
   1938  }
   1939 
   1940  RootedId id(cx);
   1941  RootedValue key(cx);
   1942  RootedValue value(cx);
   1943  Rooted<Shape*> shape(cx);
   1944  Rooted<Maybe<PropertyDescriptor>> desc(cx);
   1945  // Step 4.
   1946  size_t out = 0;
   1947  for (size_t i = 0; i < len; i++) {
   1948    id = ids[i];
   1949 
   1950    // Step 4.a. (Symbols were filtered out in step 2.)
   1951    MOZ_ASSERT(!id.isSymbol());
   1952 
   1953    if (kind != EnumerableOwnPropertiesKind::Values) {
   1954      if (!IdToStringOrSymbol(cx, id, &key)) {
   1955        return false;
   1956      }
   1957    }
   1958 
   1959    // Step 4.a.i.
   1960    if (obj->is<NativeObject>()) {
   1961      Handle<NativeObject*> nobj = obj.as<NativeObject>();
   1962      if (id.isInt() && nobj->containsDenseElement(id.toInt())) {
   1963        value.set(nobj->getDenseElement(id.toInt()));
   1964      } else {
   1965        Maybe<PropertyInfo> prop = nobj->lookup(cx, id);
   1966        if (prop.isNothing() || !prop->enumerable()) {
   1967          continue;
   1968        }
   1969        if (prop->isDataProperty()) {
   1970          value = nobj->getSlot(prop->slot());
   1971        } else if (!GetProperty(cx, obj, obj, id, &value)) {
   1972          return false;
   1973        }
   1974      }
   1975    } else {
   1976      if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
   1977        return false;
   1978      }
   1979 
   1980      // Step 4.a.ii. (inverted.)
   1981      if (desc.isNothing() || !desc->enumerable()) {
   1982        continue;
   1983      }
   1984 
   1985      // Step 4.a.ii.1.
   1986      // (Omitted because Object.keys doesn't use this implementation.)
   1987 
   1988      // Step 4.a.ii.2.a.
   1989      if (!GetProperty(cx, obj, obj, id, &value)) {
   1990        return false;
   1991      }
   1992    }
   1993 
   1994    // Steps 4.a.ii.2.b-c.
   1995    if (kind == EnumerableOwnPropertiesKind::Values) {
   1996      properties[out++].set(value);
   1997    } else if (!NewValuePair(cx, key, value, properties[out++])) {
   1998      return false;
   1999    }
   2000  }
   2001 
   2002  // Step 5.
   2003  // (Implemented in step 2.)
   2004 
   2005  // Step 3 of Object.{keys,values,entries}
   2006  JSObject* aobj = NewDenseCopiedArray(cx, out, properties.begin());
   2007  if (!aobj) {
   2008    return false;
   2009  }
   2010 
   2011  args.rval().setObject(*aobj);
   2012  return true;
   2013 }
   2014 
   2015 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
   2016 // 19.1.2.16 Object.keys ( O )
   2017 bool js::obj_keys(JSContext* cx, unsigned argc, Value* vp) {
   2018  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object", "keys");
   2019  CallArgs args = CallArgsFromVp(argc, vp);
   2020 
   2021  // Step 1.
   2022  RootedObject obj(cx, ToObject(cx, args.get(0)));
   2023  if (!obj) {
   2024    return false;
   2025  }
   2026 
   2027  bool optimized;
   2028  static constexpr EnumerableOwnPropertiesKind kind =
   2029      EnumerableOwnPropertiesKind::Keys;
   2030  if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
   2031                                              &optimized)) {
   2032    return false;
   2033  }
   2034  if (optimized) {
   2035    return true;
   2036  }
   2037 
   2038  // Steps 2-3.
   2039  return GetOwnPropertyKeys(cx, obj, JSITER_OWNONLY, args.rval());
   2040 }
   2041 
   2042 bool js::obj_keys_length(JSContext* cx, HandleObject obj, int32_t& length) {
   2043  bool optimized;
   2044  if (!CountEnumerableOwnPropertiesNative(cx, obj, length, &optimized)) {
   2045    return false;
   2046  }
   2047  if (optimized) {
   2048    return true;
   2049  }
   2050 
   2051  // Object.keys: Steps 2-3.
   2052  // (GetOwnPropertyKeys / CountOwnPropertyKeys)
   2053  RootedIdVector keys(cx);
   2054  if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) {
   2055    return false;
   2056  }
   2057 
   2058  length = keys.length();
   2059  return true;
   2060 }
   2061 
   2062 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
   2063 // 19.1.2.21 Object.values ( O )
   2064 static bool obj_values(JSContext* cx, unsigned argc, Value* vp) {
   2065  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object", "values");
   2066  CallArgs args = CallArgsFromVp(argc, vp);
   2067 
   2068  // Steps 1-3.
   2069  return EnumerableOwnProperties<EnumerableOwnPropertiesKind::Values>(cx, args);
   2070 }
   2071 
   2072 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
   2073 // 19.1.2.5 Object.entries ( O )
   2074 static bool obj_entries(JSContext* cx, unsigned argc, Value* vp) {
   2075  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object", "entries");
   2076  CallArgs args = CallArgsFromVp(argc, vp);
   2077 
   2078  // Steps 1-3.
   2079  return EnumerableOwnProperties<EnumerableOwnPropertiesKind::KeysAndValues>(
   2080      cx, args);
   2081 }
   2082 
   2083 /* ES6 draft 15.2.3.16 */
   2084 bool js::obj_is(JSContext* cx, unsigned argc, Value* vp) {
   2085  CallArgs args = CallArgsFromVp(argc, vp);
   2086 
   2087  bool same;
   2088  if (!SameValue(cx, args.get(0), args.get(1), &same)) {
   2089    return false;
   2090  }
   2091 
   2092  args.rval().setBoolean(same);
   2093  return true;
   2094 }
   2095 
   2096 bool js::IdToStringOrSymbol(JSContext* cx, HandleId id,
   2097                            MutableHandleValue result) {
   2098  if (id.isInt()) {
   2099    JSString* str = Int32ToString<CanGC>(cx, id.toInt());
   2100    if (!str) {
   2101      return false;
   2102    }
   2103    result.setString(str);
   2104  } else if (id.isAtom()) {
   2105    result.setString(id.toAtom());
   2106  } else {
   2107    result.setSymbol(id.toSymbol());
   2108  }
   2109  return true;
   2110 }
   2111 
   2112 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
   2113 // 19.1.2.10.1 Runtime Semantics: GetOwnPropertyKeys ( O, Type )
   2114 bool js::GetOwnPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags,
   2115                            MutableHandleValue rval) {
   2116  // Step 1 (Performed in caller).
   2117 
   2118  // Steps 2-4.
   2119  RootedIdVector keys(cx);
   2120  if (!GetPropertyKeys(cx, obj, flags, &keys)) {
   2121    return false;
   2122  }
   2123 
   2124  // Step 5 (Inlined CreateArrayFromList).
   2125  Rooted<ArrayObject*> array(cx,
   2126                             NewDenseFullyAllocatedArray(cx, keys.length()));
   2127  if (!array) {
   2128    return false;
   2129  }
   2130 
   2131  array->ensureDenseInitializedLength(0, keys.length());
   2132 
   2133  RootedValue val(cx);
   2134  for (size_t i = 0, len = keys.length(); i < len; i++) {
   2135    MOZ_ASSERT_IF(keys[i].isSymbol(), flags & JSITER_SYMBOLS);
   2136    MOZ_ASSERT_IF(!keys[i].isSymbol(), !(flags & JSITER_SYMBOLSONLY));
   2137    if (!IdToStringOrSymbol(cx, keys[i], &val)) {
   2138      return false;
   2139    }
   2140    array->initDenseElement(i, val);
   2141  }
   2142 
   2143  rval.setObject(*array);
   2144  return true;
   2145 }
   2146 
   2147 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
   2148 // 19.1.2.9 Object.getOwnPropertyNames ( O )
   2149 static bool obj_getOwnPropertyNames(JSContext* cx, unsigned argc, Value* vp) {
   2150  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object", "getOwnPropertyNames");
   2151  CallArgs args = CallArgsFromVp(argc, vp);
   2152 
   2153  RootedObject obj(cx, ToObject(cx, args.get(0)));
   2154  if (!obj) {
   2155    return false;
   2156  }
   2157 
   2158  bool optimized;
   2159  static constexpr EnumerableOwnPropertiesKind kind =
   2160      EnumerableOwnPropertiesKind::Names;
   2161  if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
   2162                                              &optimized)) {
   2163    return false;
   2164  }
   2165  if (optimized) {
   2166    return true;
   2167  }
   2168 
   2169  return GetOwnPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN,
   2170                            args.rval());
   2171 }
   2172 
   2173 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
   2174 // 19.1.2.10 Object.getOwnPropertySymbols ( O )
   2175 static bool obj_getOwnPropertySymbols(JSContext* cx, unsigned argc, Value* vp) {
   2176  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object", "getOwnPropertySymbols");
   2177  CallArgs args = CallArgsFromVp(argc, vp);
   2178 
   2179  RootedObject obj(cx, ToObject(cx, args.get(0)));
   2180  if (!obj) {
   2181    return false;
   2182  }
   2183 
   2184  return GetOwnPropertyKeys(
   2185      cx, obj,
   2186      JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY,
   2187      args.rval());
   2188 }
   2189 
   2190 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
   2191 static bool obj_defineProperties(JSContext* cx, unsigned argc, Value* vp) {
   2192  AutoJSMethodProfilerEntry pseudoFrame(cx, "Object", "defineProperties");
   2193  CallArgs args = CallArgsFromVp(argc, vp);
   2194 
   2195  /* Step 1. */
   2196  RootedObject obj(cx);
   2197  if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperties", &obj)) {
   2198    return false;
   2199  }
   2200 
   2201  /* Step 2. */
   2202  if (!args.requireAtLeast(cx, "Object.defineProperties", 2)) {
   2203    return false;
   2204  }
   2205 
   2206  /* Steps 3-6. */
   2207  bool failedOnWindowProxy = false;
   2208  if (!ObjectDefineProperties(cx, obj, args[1], &failedOnWindowProxy)) {
   2209    return false;
   2210  }
   2211 
   2212  /* Step 7, but modified to deal with WindowProxy mess */
   2213  if (failedOnWindowProxy) {
   2214    args.rval().setNull();
   2215  } else {
   2216    args.rval().setObject(*obj);
   2217  }
   2218  return true;
   2219 }
   2220 
   2221 // ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
   2222 static bool obj_preventExtensions(JSContext* cx, unsigned argc, Value* vp) {
   2223  CallArgs args = CallArgsFromVp(argc, vp);
   2224  args.rval().set(args.get(0));
   2225 
   2226  // Step 1.
   2227  if (!args.get(0).isObject()) {
   2228    return true;
   2229  }
   2230 
   2231  // Steps 2-5.
   2232  RootedObject obj(cx, &args.get(0).toObject());
   2233  return PreventExtensions(cx, obj);
   2234 }
   2235 
   2236 // ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
   2237 static bool obj_freeze(JSContext* cx, unsigned argc, Value* vp) {
   2238  CallArgs args = CallArgsFromVp(argc, vp);
   2239  args.rval().set(args.get(0));
   2240 
   2241  // Step 1.
   2242  if (!args.get(0).isObject()) {
   2243    return true;
   2244  }
   2245 
   2246  // Steps 2-5.
   2247  RootedObject obj(cx, &args.get(0).toObject());
   2248  return SetIntegrityLevel(cx, obj, IntegrityLevel::Frozen);
   2249 }
   2250 
   2251 // ES6 draft rev27 (2014/08/24) 19.1.2.12 Object.isFrozen(O)
   2252 static bool obj_isFrozen(JSContext* cx, unsigned argc, Value* vp) {
   2253  CallArgs args = CallArgsFromVp(argc, vp);
   2254 
   2255  // Step 1.
   2256  bool frozen = true;
   2257 
   2258  // Step 2.
   2259  if (args.get(0).isObject()) {
   2260    RootedObject obj(cx, &args.get(0).toObject());
   2261    if (!TestIntegrityLevel(cx, obj, IntegrityLevel::Frozen, &frozen)) {
   2262      return false;
   2263    }
   2264  }
   2265  args.rval().setBoolean(frozen);
   2266  return true;
   2267 }
   2268 
   2269 // ES6 draft rev27 (2014/08/24) 19.1.2.17 Object.seal(O)
   2270 static bool obj_seal(JSContext* cx, unsigned argc, Value* vp) {
   2271  CallArgs args = CallArgsFromVp(argc, vp);
   2272  args.rval().set(args.get(0));
   2273 
   2274  // Step 1.
   2275  if (!args.get(0).isObject()) {
   2276    return true;
   2277  }
   2278 
   2279  // Steps 2-5.
   2280  RootedObject obj(cx, &args.get(0).toObject());
   2281  return SetIntegrityLevel(cx, obj, IntegrityLevel::Sealed);
   2282 }
   2283 
   2284 // ES6 draft rev27 (2014/08/24) 19.1.2.13 Object.isSealed(O)
   2285 static bool obj_isSealed(JSContext* cx, unsigned argc, Value* vp) {
   2286  CallArgs args = CallArgsFromVp(argc, vp);
   2287 
   2288  // Step 1.
   2289  bool sealed = true;
   2290 
   2291  // Step 2.
   2292  if (args.get(0).isObject()) {
   2293    RootedObject obj(cx, &args.get(0).toObject());
   2294    if (!TestIntegrityLevel(cx, obj, IntegrityLevel::Sealed, &sealed)) {
   2295      return false;
   2296    }
   2297  }
   2298  args.rval().setBoolean(sealed);
   2299  return true;
   2300 }
   2301 
   2302 bool js::obj_setProto(JSContext* cx, unsigned argc, Value* vp) {
   2303  CallArgs args = CallArgsFromVp(argc, vp);
   2304  MOZ_ASSERT(args.length() == 1);
   2305 
   2306  HandleValue thisv = args.thisv();
   2307  if (thisv.isNullOrUndefined()) {
   2308    ReportIncompatible(cx, args);
   2309    return false;
   2310  }
   2311  if (thisv.isPrimitive()) {
   2312    // Mutating a boxed primitive's [[Prototype]] has no side effects.
   2313    args.rval().setUndefined();
   2314    return true;
   2315  }
   2316 
   2317  /* Do nothing if __proto__ isn't being set to an object or null. */
   2318  if (!args[0].isObjectOrNull()) {
   2319    args.rval().setUndefined();
   2320    return true;
   2321  }
   2322 
   2323  Rooted<JSObject*> obj(cx, &args.thisv().toObject());
   2324  Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull());
   2325  if (!SetPrototype(cx, obj, newProto)) {
   2326    return false;
   2327  }
   2328 
   2329  args.rval().setUndefined();
   2330  return true;
   2331 }
   2332 
   2333 static const JSFunctionSpec object_methods[] = {
   2334    JS_FN("toSource", obj_toSource, 0, 0),
   2335    JS_INLINABLE_FN("toString", obj_toString, 0, 0, ObjectToString),
   2336    JS_SELF_HOSTED_FN("toLocaleString", "Object_toLocaleString", 0, 0),
   2337    JS_SELF_HOSTED_FN("valueOf", "Object_valueOf", 0, 0),
   2338    JS_SELF_HOSTED_FN("hasOwnProperty", "Object_hasOwnProperty", 1, 0),
   2339    JS_INLINABLE_FN("isPrototypeOf", obj_isPrototypeOf, 1, 0,
   2340                    ObjectIsPrototypeOf),
   2341    JS_FN("propertyIsEnumerable", obj_propertyIsEnumerable, 1, 0),
   2342    JS_SELF_HOSTED_FN("__defineGetter__", "ObjectDefineGetter", 2, 0),
   2343    JS_SELF_HOSTED_FN("__defineSetter__", "ObjectDefineSetter", 2, 0),
   2344    JS_SELF_HOSTED_FN("__lookupGetter__", "ObjectLookupGetter", 1, 0),
   2345    JS_SELF_HOSTED_FN("__lookupSetter__", "ObjectLookupSetter", 1, 0),
   2346    JS_FS_END,
   2347 };
   2348 
   2349 static const JSPropertySpec object_properties[] = {
   2350    JS_SELF_HOSTED_GETSET("__proto__", "$ObjectProtoGetter",
   2351                          "$ObjectProtoSetter", 0),
   2352    JS_PS_END,
   2353 };
   2354 
   2355 static const JSFunctionSpec object_static_methods[] = {
   2356    JS_FN("assign", obj_assign, 2, 0),
   2357    JS_SELF_HOSTED_FN("getPrototypeOf", "ObjectGetPrototypeOf", 1, 0),
   2358    JS_FN("setPrototypeOf", obj_setPrototypeOf, 2, 0),
   2359    JS_SELF_HOSTED_FN("getOwnPropertyDescriptor",
   2360                      "ObjectGetOwnPropertyDescriptor", 2, 0),
   2361    JS_SELF_HOSTED_FN("getOwnPropertyDescriptors",
   2362                      "ObjectGetOwnPropertyDescriptors", 1, 0),
   2363    JS_INLINABLE_FN("keys", obj_keys, 1, 0, ObjectKeys),
   2364    JS_FN("values", obj_values, 1, 0),
   2365    JS_FN("entries", obj_entries, 1, 0),
   2366    JS_INLINABLE_FN("is", obj_is, 2, 0, ObjectIs),
   2367    JS_SELF_HOSTED_FN("defineProperty", "ObjectDefineProperty", 3, 0),
   2368    JS_FN("defineProperties", obj_defineProperties, 2, 0),
   2369    JS_INLINABLE_FN("create", obj_create, 2, 0, ObjectCreate),
   2370    JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1, 0),
   2371    JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1, 0),
   2372    JS_SELF_HOSTED_FN("isExtensible", "ObjectIsExtensible", 1, 0),
   2373    JS_FN("preventExtensions", obj_preventExtensions, 1, 0),
   2374    JS_FN("freeze", obj_freeze, 1, 0),
   2375    JS_FN("isFrozen", obj_isFrozen, 1, 0),
   2376    JS_FN("seal", obj_seal, 1, 0),
   2377    JS_FN("isSealed", obj_isSealed, 1, 0),
   2378    JS_SELF_HOSTED_FN("fromEntries", "ObjectFromEntries", 1, 0),
   2379    JS_SELF_HOSTED_FN("hasOwn", "ObjectHasOwn", 2, 0),
   2380    JS_SELF_HOSTED_FN("groupBy", "ObjectGroupBy", 2, 0),
   2381    JS_FS_END,
   2382 };
   2383 
   2384 static JSObject* CreateObjectConstructor(JSContext* cx, JSProtoKey key) {
   2385  Rooted<GlobalObject*> self(cx, cx->global());
   2386  if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function)) {
   2387    return nullptr;
   2388  }
   2389 
   2390  /* Create the Object function now that we have a [[Prototype]] for it. */
   2391  JSFunction* fun = NewNativeConstructor(
   2392      cx, obj_construct, 1, Handle<PropertyName*>(cx->names().Object),
   2393      gc::AllocKind::FUNCTION, TenuredObject);
   2394  if (!fun) {
   2395    return nullptr;
   2396  }
   2397 
   2398  fun->setJitInfo(&jit::JitInfo_Object);
   2399  return fun;
   2400 }
   2401 
   2402 static JSObject* CreateObjectPrototype(JSContext* cx, JSProtoKey key) {
   2403  MOZ_ASSERT(!cx->zone()->isAtomsZone());
   2404  MOZ_ASSERT(cx->global()->is<NativeObject>());
   2405 
   2406  /*
   2407   * Create |Object.prototype| first, mirroring CreateBlankProto but for the
   2408   * prototype of the created object.
   2409   */
   2410  Rooted<PlainObject*> objectProto(
   2411      cx, NewPlainObjectWithProto(cx, nullptr, TenuredObject));
   2412  if (!objectProto) {
   2413    return nullptr;
   2414  }
   2415 
   2416  bool succeeded;
   2417  if (!SetImmutablePrototype(cx, objectProto, &succeeded)) {
   2418    return nullptr;
   2419  }
   2420  MOZ_ASSERT(succeeded,
   2421             "should have been able to make a fresh Object.prototype's "
   2422             "[[Prototype]] immutable");
   2423 
   2424  return objectProto;
   2425 }
   2426 
   2427 static bool FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor,
   2428                                  JS::HandleObject proto) {
   2429  Rooted<GlobalObject*> global(cx, cx->global());
   2430 
   2431  // ES5 15.1.2.1.
   2432  RootedId evalId(cx, NameToId(cx->names().eval));
   2433  JSFunction* evalobj =
   2434      DefineFunction(cx, global, evalId, IndirectEval, 1, JSPROP_RESOLVING);
   2435  if (!evalobj) {
   2436    return false;
   2437  }
   2438  global->setOriginalEval(evalobj);
   2439 
   2440 #ifdef FUZZING
   2441  if (cx->options().fuzzing()) {
   2442    if (!DefineTestingFunctions(cx, global, /* fuzzingSafe = */ true,
   2443                                /* disableOOMFunctions = */ false)) {
   2444      return false;
   2445    }
   2446  }
   2447 #endif
   2448 
   2449  // The global object should have |Object.prototype| as its [[Prototype]].
   2450  MOZ_ASSERT(global->staticPrototype() == nullptr);
   2451  MOZ_ASSERT(!global->staticPrototypeIsImmutable());
   2452  return SetPrototype(cx, global, proto);
   2453 }
   2454 
   2455 static const ClassSpec PlainObjectClassSpec = {
   2456    CreateObjectConstructor, CreateObjectPrototype,
   2457    object_static_methods,   nullptr,
   2458    object_methods,          object_properties,
   2459    FinishObjectClassInit,
   2460 };
   2461 
   2462 const JSClass PlainObject::class_ = {
   2463    "Object",
   2464    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
   2465    JS_NULL_CLASS_OPS,
   2466    &PlainObjectClassSpec,
   2467 };
   2468 
   2469 const JSClass* const js::ObjectClassPtr = &PlainObject::class_;