/ runtime / ArrayPrototype.cpp
ArrayPrototype.cpp
   1  /*
   2   *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
   3   *  Copyright (C) 2003-2020 Apple Inc. All rights reserved.
   4   *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
   5   *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
   6   *
   7   *  This library is free software; you can redistribute it and/or
   8   *  modify it under the terms of the GNU Lesser General Public
   9   *  License as published by the Free Software Foundation; either
  10   *  version 2 of the License, or (at your option) any later version.
  11   *
  12   *  This library is distributed in the hope that it will be useful,
  13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15   *  Lesser General Public License for more details.
  16   *
  17   *  You should have received a copy of the GNU Lesser General Public
  18   *  License along with this library; if not, write to the Free Software
  19   *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
  20   *  USA
  21   *
  22   */
  23  
  24  #include "config.h"
  25  #include "ArrayPrototype.h"
  26  
  27  #include "ArrayConstructor.h"
  28  #include "BuiltinNames.h"
  29  #include "IntegrityInlines.h"
  30  #include "JSArrayInlines.h"
  31  #include "JSArrayIterator.h"
  32  #include "JSCBuiltins.h"
  33  #include "JSCInlines.h"
  34  #include "JSImmutableButterfly.h"
  35  #include "JSStringJoiner.h"
  36  #include "ObjectConstructor.h"
  37  #include "StringRecursionChecker.h"
  38  #include <algorithm>
  39  #include <wtf/Assertions.h>
  40  
  41  namespace JSC {
  42  
  43  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncToLocaleString);
  44  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncJoin);
  45  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncKeys);
  46  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncEntries);
  47  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncPop);
  48  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncPush);
  49  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncReverse);
  50  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncShift);
  51  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncSlice);
  52  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncSplice);
  53  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncUnShift);
  54  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncIndexOf);
  55  static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncLastIndexOf);
  56  
  57  // ------------------------------ ArrayPrototype ----------------------------
  58  
  59  const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ArrayPrototype)};
  60  
  61  ArrayPrototype* ArrayPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
  62  {
  63      ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(vm.heap)) ArrayPrototype(vm, structure);
  64      prototype->finishCreation(vm, globalObject);
  65      return prototype;
  66  }
  67  
  68  // ECMA 15.4.4
  69  ArrayPrototype::ArrayPrototype(VM& vm, Structure* structure)
  70      : JSArray(vm, structure, nullptr)
  71  {
  72  }
  73  
  74  void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
  75  {
  76      Base::finishCreation(vm);
  77      ASSERT(inherits(vm, info()));
  78  
  79      putDirectWithoutTransition(vm, vm.propertyNames->toString, globalObject->arrayProtoToStringFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
  80      putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
  81      putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
  82  
  83      JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
  84      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().concatPublicName(), arrayPrototypeConcatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
  85      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().fillPublicName(), arrayPrototypeFillCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
  86      JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
  87      JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, ArrayPopIntrinsic);
  88      JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPublicName(), arrayProtoFuncPush, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayPushIntrinsic);
  89      JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
  90      JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
  91      JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, 0);
  92      JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, ArraySliceIntrinsic);
  93      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().sortPublicName(), arrayPrototypeSortCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
  94      JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
  95      JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
  96      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().everyPublicName(), arrayPrototypeEveryCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
  97      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().forEachPublicName(), arrayPrototypeForEachCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
  98      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().somePublicName(), arrayPrototypeSomeCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
  99      JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayIndexOfIntrinsic);
 100      JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
 101      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().filterPublicName(), arrayPrototypeFilterCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 102      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().flatPublicName(), arrayPrototypeFlatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 103      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().flatMapPublicName(), arrayPrototypeFlatMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 104      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().reducePublicName(), arrayPrototypeReduceCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 105      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().reduceRightPublicName(), arrayPrototypeReduceRightCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 106      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().mapPublicName(), arrayPrototypeMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 107      JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), arrayProtoFuncKeys, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, ArrayKeysIntrinsic);
 108      JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), arrayProtoFuncEntries, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, ArrayEntriesIntrinsic);
 109      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findPublicName(), arrayPrototypeFindCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 110      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findIndexPublicName(), arrayPrototypeFindIndexCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 111      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().includesPublicName(), arrayPrototypeIncludesCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 112      JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().copyWithinPublicName(), arrayPrototypeCopyWithinCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 113  
 114      if (Options::useAtMethod())
 115          JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().atPublicName(), arrayPrototypeAtCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 116  
 117      putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
 118      putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().forEachPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().forEachPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
 119      putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().keysPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().keysPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
 120      putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPrivateName(), globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::ReadOnly));
 121  
 122      JSObject* unscopables = constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure());
 123      unscopables->convertToDictionary(vm);
 124      const Identifier* const unscopableNames[] = {
 125          &vm.propertyNames->builtinNames().copyWithinPublicName(),
 126          &vm.propertyNames->builtinNames().entriesPublicName(),
 127          &vm.propertyNames->builtinNames().fillPublicName(),
 128          &vm.propertyNames->builtinNames().findPublicName(),
 129          &vm.propertyNames->builtinNames().findIndexPublicName(),
 130          &vm.propertyNames->builtinNames().flatPublicName(),
 131          &vm.propertyNames->builtinNames().flatMapPublicName(),
 132          &vm.propertyNames->builtinNames().includesPublicName(),
 133          &vm.propertyNames->builtinNames().keysPublicName(),
 134          &vm.propertyNames->builtinNames().valuesPublicName()
 135      };
 136      if (Options::useAtMethod())
 137          unscopables->putDirect(vm, vm.propertyNames->builtinNames().atPublicName(), jsBoolean(true));
 138      for (const auto* unscopableName : unscopableNames)
 139          unscopables->putDirect(vm, *unscopableName, jsBoolean(true));
 140      putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, unscopables, PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
 141  }
 142  
 143  // ------------------------------ Array Functions ----------------------------
 144  
 145  static ALWAYS_INLINE JSValue getProperty(JSGlobalObject* globalObject, JSObject* object, uint64_t index)
 146  {
 147      VM& vm = globalObject->vm();
 148      auto scope = DECLARE_THROW_SCOPE(vm);
 149      
 150      if (JSValue result = object->tryGetIndexQuickly(index))
 151          return result;
 152      // We want to perform get and has in the same operation.
 153      // We can only do so when this behavior is not observable. The
 154      // only time it is observable is when we encounter an opaque objects (ProxyObject and JSModuleNamespaceObject)
 155      // somewhere in the prototype chain.
 156      PropertySlot slot(object, PropertySlot::InternalMethodType::HasProperty);
 157      bool hasProperty = object->getPropertySlot(globalObject, index, slot);
 158      EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
 159      if (!hasProperty)
 160          return { };
 161      if (UNLIKELY(slot.isTaintedByOpaqueObject()))
 162          RELEASE_AND_RETURN(scope, object->get(globalObject, index));
 163  
 164      RELEASE_AND_RETURN(scope, slot.getValue(globalObject, index));
 165  }
 166  
 167  static ALWAYS_INLINE void setLength(JSGlobalObject* globalObject, VM& vm, JSObject* obj, uint64_t value)
 168  {
 169      auto scope = DECLARE_THROW_SCOPE(vm);
 170      static constexpr bool throwException = true;
 171      if (LIKELY(isJSArray(obj))) {
 172          if (UNLIKELY(value > UINT32_MAX)) {
 173              throwRangeError(globalObject, scope, "Invalid array length"_s);
 174              return;
 175          }
 176          scope.release();
 177          jsCast<JSArray*>(obj)->setLength(globalObject, static_cast<uint32_t>(value), throwException);
 178          return;
 179      }
 180      scope.release();
 181      PutPropertySlot slot(obj, throwException);
 182      obj->methodTable(vm)->put(obj, globalObject, vm.propertyNames->length, jsNumber(value), slot);
 183  }
 184  
 185  namespace ArrayPrototypeInternal {
 186  static bool verbose = false;
 187  }
 188  
 189  static ALWAYS_INLINE bool speciesWatchpointIsValid(VM& vm, JSObject* thisObject)
 190  {
 191      JSGlobalObject* globalObject = thisObject->globalObject(vm);
 192      ArrayPrototype* arrayPrototype = globalObject->arrayPrototype();
 193  
 194      if (globalObject->arraySpeciesWatchpointSet().stateOnJSThread() == ClearWatchpoint) {
 195          dataLogLnIf(ArrayPrototypeInternal::verbose, "Initializing Array species watchpoints for Array.prototype: ", pointerDump(arrayPrototype), " with structure: ", pointerDump(arrayPrototype->structure(vm)), "\nand Array: ", pointerDump(globalObject->arrayConstructor()), " with structure: ", pointerDump(globalObject->arrayConstructor()->structure(vm)));
 196          globalObject->tryInstallArraySpeciesWatchpoint();
 197          ASSERT(globalObject->arraySpeciesWatchpointSet().stateOnJSThread() != ClearWatchpoint);
 198      }
 199  
 200      return !thisObject->hasCustomProperties(vm)
 201          && arrayPrototype == thisObject->getPrototypeDirect(vm)
 202          && globalObject->arraySpeciesWatchpointSet().stateOnJSThread() == IsWatched;
 203  }
 204  
 205  enum class SpeciesConstructResult {
 206      FastPath,
 207      Exception,
 208      CreatedObject
 209  };
 210  
 211  static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstructArray(JSGlobalObject* globalObject, JSObject* thisObject, uint64_t length)
 212  {
 213      VM& vm = globalObject->vm();
 214      auto scope = DECLARE_THROW_SCOPE(vm);
 215  
 216      auto exceptionResult = [] () {
 217          return std::make_pair(SpeciesConstructResult::Exception, nullptr);
 218      };
 219  
 220      // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
 221      JSValue constructor = jsUndefined();
 222      bool thisIsArray = isArray(globalObject, thisObject);
 223      RETURN_IF_EXCEPTION(scope, exceptionResult());
 224      if (LIKELY(thisIsArray)) {
 225          // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
 226          // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
 227          bool isValid = speciesWatchpointIsValid(vm, thisObject);
 228          scope.assertNoException();
 229          if (LIKELY(isValid))
 230              return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
 231  
 232          constructor = thisObject->get(globalObject, vm.propertyNames->constructor);
 233          RETURN_IF_EXCEPTION(scope, exceptionResult());
 234          if (constructor.isConstructor(vm)) {
 235              JSObject* constructorObject = jsCast<JSObject*>(constructor);
 236              bool isArrayConstructorFromAnotherRealm = globalObject != constructorObject->globalObject(vm)
 237                  && constructorObject->inherits<ArrayConstructor>(vm);
 238              if (isArrayConstructorFromAnotherRealm)
 239                  return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
 240          }
 241          if (constructor.isObject()) {
 242              constructor = constructor.get(globalObject, vm.propertyNames->speciesSymbol);
 243              RETURN_IF_EXCEPTION(scope, exceptionResult());
 244              if (constructor.isNull())
 245                  return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
 246          }
 247      } else {
 248          // If isArray is false, return ? ArrayCreate(length).
 249          return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
 250      }
 251  
 252      if (constructor.isUndefined())
 253          return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
 254  
 255      MarkedArgumentBuffer args;
 256      args.append(jsNumber(length));
 257      ASSERT(!args.hasOverflowed());
 258      JSObject* newObject = construct(globalObject, constructor, args, "Species construction did not get a valid constructor");
 259      RETURN_IF_EXCEPTION(scope, exceptionResult());
 260      return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
 261  }
 262  
 263  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncSpeciesCreate, (JSGlobalObject* globalObject, CallFrame* callFrame))
 264  {
 265      VM& vm = globalObject->vm();
 266      auto scope = DECLARE_THROW_SCOPE(vm);
 267      JSObject* object = asObject(callFrame->uncheckedArgument(0));
 268      uint64_t length = static_cast<uint64_t>(callFrame->uncheckedArgument(1).asNumber());
 269  
 270      std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(globalObject, object, length);
 271      EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
 272      if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
 273          return { };
 274      if (speciesResult.first == SpeciesConstructResult::CreatedObject)
 275          return JSValue::encode(speciesResult.second);
 276  
 277      if (length > std::numeric_limits<unsigned>::max()) {
 278          throwRangeError(globalObject, scope, "Array size is not a small enough positive integer."_s);
 279          return { };
 280      }
 281  
 282      RELEASE_AND_RETURN(scope, JSValue::encode(constructEmptyArray(globalObject, nullptr, static_cast<unsigned>(length))));
 283  }
 284  
 285  static inline uint64_t argumentClampedIndexFromStartOrEnd(JSGlobalObject* globalObject, JSValue value, uint64_t length, uint64_t undefinedValue = 0)
 286  {
 287      if (value.isUndefined())
 288          return undefinedValue;
 289  
 290      double indexDouble = value.toInteger(globalObject);
 291      if (indexDouble < 0) {
 292          indexDouble += length;
 293          return indexDouble < 0 ? 0 : static_cast<uint64_t>(indexDouble);
 294      }
 295      return indexDouble > length ? length : static_cast<uint64_t>(indexDouble);
 296  }
 297  
 298  // The shift/unshift function implement the shift/unshift behaviour required
 299  // by the corresponding array prototype methods, and by splice. In both cases,
 300  // the methods are operating an an array or array like object.
 301  //
 302  //  header  currentCount  (remainder)
 303  // [------][------------][-----------]
 304  //  header  resultCount  (remainder)
 305  // [------][-----------][-----------]
 306  //
 307  // The set of properties in the range 'header' must be unchanged. The set of
 308  // properties in the range 'remainder' (where remainder = length - header -
 309  // currentCount) will be shifted to the left or right as appropriate; in the
 310  // case of shift this must be removing values, in the case of unshift this
 311  // must be introducing new values.
 312  
 313  template<JSArray::ShiftCountMode shiftCountMode>
 314  void shift(JSGlobalObject* globalObject, JSObject* thisObj, uint64_t header, uint64_t currentCount, uint64_t resultCount, uint64_t length)
 315  {
 316      VM& vm = globalObject->vm();
 317      auto scope = DECLARE_THROW_SCOPE(vm);
 318  
 319      RELEASE_ASSERT(currentCount > resultCount);
 320      uint64_t count = currentCount - resultCount;
 321  
 322      RELEASE_ASSERT(header <= length);
 323      RELEASE_ASSERT(currentCount <= (length - header));
 324  
 325      if (isJSArray(thisObj)) {
 326          JSArray* array = asArray(thisObj);
 327          uint32_t header32 = static_cast<uint32_t>(header);
 328          ASSERT(header32 == header);
 329          if (array->length() == length && array->shiftCount<shiftCountMode>(globalObject, header32, static_cast<uint32_t>(count)))
 330              return;
 331          header = header32;
 332      }
 333  
 334      for (uint64_t k = header; k < length - currentCount; ++k) {
 335          uint64_t from = k + currentCount;
 336          uint64_t to = k + resultCount;
 337          JSValue value = getProperty(globalObject, thisObj, from);
 338          RETURN_IF_EXCEPTION(scope, void());
 339          if (value) {
 340              thisObj->putByIndexInline(globalObject, to, value, true);
 341              RETURN_IF_EXCEPTION(scope, void());
 342          } else {
 343              bool success = thisObj->deleteProperty(globalObject, to);
 344              RETURN_IF_EXCEPTION(scope, void());
 345              if (!success) {
 346                  throwTypeError(globalObject, scope, UnableToDeletePropertyError);
 347                  return;
 348              }
 349          }
 350      }
 351      for (uint64_t k = length; k > length - count; --k) {
 352          bool success = thisObj->deleteProperty(globalObject, k - 1);
 353          RETURN_IF_EXCEPTION(scope, void());
 354          if (!success) {
 355              throwTypeError(globalObject, scope, UnableToDeletePropertyError);
 356              return;
 357          }
 358      }
 359  }
 360  
 361  template<JSArray::ShiftCountMode shiftCountMode>
 362  void unshift(JSGlobalObject* globalObject, JSObject* thisObj, uint64_t header, uint64_t currentCount, uint64_t resultCount, uint64_t length)
 363  {
 364      ASSERT(header <= maxSafeInteger());
 365      ASSERT(currentCount <= maxSafeInteger());
 366      ASSERT(resultCount <= maxSafeInteger());
 367      ASSERT(length <= maxSafeInteger());
 368  
 369      VM& vm = globalObject->vm();
 370      auto scope = DECLARE_THROW_SCOPE(vm);
 371  
 372      RELEASE_ASSERT(resultCount > currentCount);
 373      uint64_t count = resultCount - currentCount;
 374  
 375      RELEASE_ASSERT(header <= length);
 376      RELEASE_ASSERT(currentCount <= (length - header));
 377  
 378      if (isJSArray(thisObj)) {
 379          // Spec says if we would produce an array of this size, we must throw a range error.
 380          if (count + length > std::numeric_limits<uint32_t>::max()) {
 381              throwRangeError(globalObject, scope, LengthExceededTheMaximumArrayLengthError);
 382              return;
 383          }
 384  
 385          JSArray* array = asArray(thisObj);
 386          if (array->length() == length) {
 387              bool handled = array->unshiftCount<shiftCountMode>(globalObject, static_cast<uint32_t>(header), static_cast<uint32_t>(count));
 388              EXCEPTION_ASSERT(!scope.exception() || handled);
 389              if (handled)
 390                  return;
 391          }
 392      }
 393  
 394      for (uint64_t k = length - currentCount; k > header; --k) {
 395          uint64_t from = k + currentCount - 1;
 396          uint64_t to = k + resultCount - 1;
 397          JSValue value = getProperty(globalObject, thisObj, from);
 398          RETURN_IF_EXCEPTION(scope, void());
 399          if (value) {
 400              thisObj->putByIndexInline(globalObject, to, value, true);
 401              RETURN_IF_EXCEPTION(scope, void());
 402          } else {
 403              bool success = thisObj->deleteProperty(globalObject, to);
 404              RETURN_IF_EXCEPTION(scope, void());
 405              if (UNLIKELY(!success)) {
 406                  throwTypeError(globalObject, scope, UnableToDeletePropertyError);
 407                  return;
 408              }
 409          }
 410      }
 411  }
 412  
 413  inline bool canUseFastJoin(const JSObject* thisObject)
 414  {
 415      switch (thisObject->indexingType()) {
 416      case ALL_CONTIGUOUS_INDEXING_TYPES:
 417      case ALL_INT32_INDEXING_TYPES:
 418      case ALL_DOUBLE_INDEXING_TYPES:
 419      case ALL_UNDECIDED_INDEXING_TYPES:
 420          return true;
 421      default:
 422          break;
 423      }
 424      return false;
 425  }
 426  
 427  inline bool holesMustForwardToPrototype(VM& vm, JSObject* object)
 428  {
 429      return object->structure(vm)->holesMustForwardToPrototype(vm, object);
 430  }
 431  
 432  inline bool isHole(double value)
 433  {
 434      return std::isnan(value);
 435  }
 436  
 437  inline bool isHole(const WriteBarrier<Unknown>& value)
 438  {
 439      return !value;
 440  }
 441  
 442  template<typename T>
 443  inline bool containsHole(T* data, unsigned length)
 444  {
 445      for (unsigned i = 0; i < length; ++i) {
 446          if (isHole(data[i]))
 447              return true;
 448      }
 449      return false;
 450  }
 451  
 452  inline JSValue fastJoin(JSGlobalObject* globalObject, JSObject* thisObject, StringView separator, unsigned length, bool& sawHoles, bool& genericCase)
 453  {
 454      VM& vm = globalObject->vm();
 455      auto scope = DECLARE_THROW_SCOPE(vm);
 456  
 457      switch (thisObject->indexingType()) {
 458      case ALL_INT32_INDEXING_TYPES: {
 459          auto& butterfly = *thisObject->butterfly();
 460          if (UNLIKELY(length > butterfly.publicLength()))
 461              break;
 462          JSStringJoiner joiner(globalObject, separator, length);
 463          RETURN_IF_EXCEPTION(scope, { });
 464          auto data = butterfly.contiguous().data();
 465          bool holesKnownToBeOK = false;
 466          for (unsigned i = 0; i < length; ++i) {
 467              JSValue value = data[i].get();
 468              if (LIKELY(value))
 469                  joiner.appendNumber(vm, value.asInt32());
 470              else {
 471                  sawHoles = true;
 472                  if (!holesKnownToBeOK) {
 473                      if (holesMustForwardToPrototype(vm, thisObject))
 474                          goto generalCase;
 475                      holesKnownToBeOK = true;
 476                  }
 477                  joiner.appendEmptyString();
 478              }
 479          }
 480          RELEASE_AND_RETURN(scope, joiner.join(globalObject));
 481      }
 482      case ALL_CONTIGUOUS_INDEXING_TYPES: {
 483          auto& butterfly = *thisObject->butterfly();
 484          if (UNLIKELY(length > butterfly.publicLength()))
 485              break;
 486          JSStringJoiner joiner(globalObject, separator, length);
 487          RETURN_IF_EXCEPTION(scope, { });
 488          auto data = butterfly.contiguous().data();
 489          bool holesKnownToBeOK = false;
 490          for (unsigned i = 0; i < length; ++i) {
 491              if (JSValue value = data[i].get()) {
 492                  if (!joiner.appendWithoutSideEffects(globalObject, value))
 493                      goto generalCase;
 494                  RETURN_IF_EXCEPTION(scope, { });
 495              } else {
 496                  sawHoles = true;
 497                  if (!holesKnownToBeOK) {
 498                      if (holesMustForwardToPrototype(vm, thisObject))
 499                          goto generalCase;
 500                      holesKnownToBeOK = true;
 501                  }
 502                  joiner.appendEmptyString();
 503              }
 504          }
 505          RELEASE_AND_RETURN(scope, joiner.join(globalObject));
 506      }
 507      case ALL_DOUBLE_INDEXING_TYPES: {
 508          auto& butterfly = *thisObject->butterfly();
 509          if (UNLIKELY(length > butterfly.publicLength()))
 510              break;
 511          JSStringJoiner joiner(globalObject, separator, length);
 512          RETURN_IF_EXCEPTION(scope, { });
 513          auto data = butterfly.contiguousDouble().data();
 514          bool holesKnownToBeOK = false;
 515          for (unsigned i = 0; i < length; ++i) {
 516              double value = data[i];
 517              if (LIKELY(!isHole(value)))
 518                  joiner.appendNumber(vm, value);
 519              else {
 520                  sawHoles = true;
 521                  if (!holesKnownToBeOK) {
 522                      if (holesMustForwardToPrototype(vm, thisObject))
 523                          goto generalCase;
 524                      holesKnownToBeOK = true;
 525                  }
 526                  joiner.appendEmptyString();
 527              }
 528          }
 529          RELEASE_AND_RETURN(scope, joiner.join(globalObject));
 530      }
 531      case ALL_UNDECIDED_INDEXING_TYPES: {
 532          if (length && holesMustForwardToPrototype(vm, thisObject))
 533              goto generalCase;
 534          switch (separator.length()) {
 535          case 0:
 536              RELEASE_AND_RETURN(scope, jsEmptyString(vm));
 537          case 1: {
 538              if (length <= 1)
 539                  RELEASE_AND_RETURN(scope, jsEmptyString(vm));
 540              if (separator.is8Bit())
 541                  RELEASE_AND_RETURN(scope, repeatCharacter(globalObject, separator.characters8()[0], length - 1));
 542              RELEASE_AND_RETURN(scope, repeatCharacter(globalObject, separator.characters16()[0], length - 1));
 543          default:
 544              JSString* result = jsEmptyString(vm);
 545              if (length <= 1)
 546                  return result;
 547  
 548              JSString* operand = jsString(vm, separator.toString());
 549              RETURN_IF_EXCEPTION(scope, { });
 550              unsigned count = length - 1;
 551              for (;;) {
 552                  if (count & 1) {
 553                      result = jsString(globalObject, result, operand);
 554                      RETURN_IF_EXCEPTION(scope, { });
 555                  }
 556                  count >>= 1;
 557                  if (!count)
 558                      return result;
 559                  operand = jsString(globalObject, operand, operand);
 560                  RETURN_IF_EXCEPTION(scope, { });
 561              }
 562          }
 563          }
 564      }
 565      }
 566  
 567  generalCase:
 568      genericCase = true;
 569      JSStringJoiner joiner(globalObject, separator, length);
 570      RETURN_IF_EXCEPTION(scope, { });
 571      for (unsigned i = 0; i < length; ++i) {
 572          JSValue element = thisObject->getIndex(globalObject, i);
 573          RETURN_IF_EXCEPTION(scope, { });
 574          joiner.append(globalObject, element);
 575          RETURN_IF_EXCEPTION(scope, { });
 576      }
 577      RELEASE_AND_RETURN(scope, joiner.join(globalObject));
 578  }
 579  
 580  ALWAYS_INLINE JSValue fastJoin(JSGlobalObject* globalObject, JSObject* thisObject, StringView separator, unsigned length)
 581  {
 582      bool sawHoles = false;
 583      bool genericCase = false;
 584      return fastJoin(globalObject, thisObject, separator, length, sawHoles, genericCase);
 585  }
 586  
 587  inline bool canUseDefaultArrayJoinForToString(VM& vm, JSObject* thisObject)
 588  {
 589      JSGlobalObject* globalObject = thisObject->globalObject();
 590  
 591      if (globalObject->arrayJoinWatchpointSet().stateOnJSThread() != IsWatched)
 592          return false;
 593  
 594      Structure* structure = thisObject->structure(vm);
 595  
 596      // This is the fast case. Many arrays will be an original array.
 597      // We are doing very simple check here. If we do more complicated checks like looking into getDirect "join" of thisObject,
 598      // it would be possible that just looking into "join" function will show the same performance.
 599      return globalObject->isOriginalArrayStructure(structure);
 600  }
 601  
 602  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncToString, (JSGlobalObject* globalObject, CallFrame* callFrame))
 603  {
 604      VM& vm = globalObject->vm();
 605      auto scope = DECLARE_THROW_SCOPE(vm);
 606      JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict());
 607  
 608      // 1. Let array be the result of calling ToObject on the this value.
 609      JSObject* thisObject = thisValue.toObject(globalObject);
 610      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 611  
 612      Integrity::auditStructureID(vm, thisObject->structureID());
 613      if (!canUseDefaultArrayJoinForToString(vm, thisObject)) {
 614          // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
 615          JSValue function = JSValue(thisObject).get(globalObject, vm.propertyNames->join);
 616          RETURN_IF_EXCEPTION(scope, encodedJSValue());
 617  
 618          // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
 619          bool customJoinCase = false;
 620          if (!function.isCell())
 621              customJoinCase = true;
 622          auto callData = getCallData(vm, function);
 623          if (callData.type == CallData::Type::None)
 624              customJoinCase = true;
 625  
 626          if (UNLIKELY(customJoinCase))
 627              RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(globalObject, "[object ", thisObject->methodTable(vm)->className(thisObject, vm), "]")));
 628  
 629          // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
 630          if (!isJSArray(thisObject) || callData.type != CallData::Type::Native || callData.native.function != arrayProtoFuncJoin)
 631              RELEASE_AND_RETURN(scope, JSValue::encode(call(globalObject, function, callData, thisObject, *vm.emptyList)));
 632      }
 633  
 634      ASSERT(isJSArray(thisValue));
 635      JSArray* thisArray = asArray(thisValue);
 636  
 637      unsigned length = thisArray->length();
 638  
 639      StringRecursionChecker checker(globalObject, thisArray);
 640      EXCEPTION_ASSERT(!scope.exception() || checker.earlyReturnValue());
 641      if (JSValue earlyReturnValue = checker.earlyReturnValue())
 642          return JSValue::encode(earlyReturnValue);
 643  
 644      if (LIKELY(canUseFastJoin(thisArray))) {
 645          const LChar comma = ',';
 646          scope.release();
 647  
 648          bool isCoW = isCopyOnWrite(thisArray->indexingMode());
 649          JSImmutableButterfly* immutableButterfly = nullptr;
 650          if (isCoW) {
 651              immutableButterfly = JSImmutableButterfly::fromButterfly(thisArray->butterfly());
 652              auto iter = vm.heap.immutableButterflyToStringCache.find(immutableButterfly);
 653              if (iter != vm.heap.immutableButterflyToStringCache.end())
 654                  return JSValue::encode(iter->value);
 655          }
 656  
 657          bool sawHoles = false;
 658          bool genericCase = false;
 659          JSValue result = fastJoin(globalObject, thisArray, { &comma, 1 }, length, sawHoles, genericCase);
 660          RETURN_IF_EXCEPTION(scope, { });
 661  
 662          if (!sawHoles && !genericCase && result && isJSString(result) && isCoW) {
 663              ASSERT(JSImmutableButterfly::fromButterfly(thisArray->butterfly()) == immutableButterfly);
 664              vm.heap.immutableButterflyToStringCache.add(immutableButterfly, jsCast<JSString*>(result));
 665          }
 666  
 667          return JSValue::encode(result);
 668      }
 669  
 670      JSStringJoiner joiner(globalObject, ',', length);
 671      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 672  
 673      for (unsigned i = 0; i < length; ++i) {
 674          JSValue element = thisArray->tryGetIndexQuickly(i);
 675          if (!element) {
 676              element = thisArray->get(globalObject, i);
 677              RETURN_IF_EXCEPTION(scope, encodedJSValue());
 678          }
 679          joiner.append(globalObject, element);
 680          RETURN_IF_EXCEPTION(scope, encodedJSValue());
 681      }
 682  
 683      RELEASE_AND_RETURN(scope, JSValue::encode(joiner.join(globalObject)));
 684  }
 685  
 686  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncToLocaleString, (JSGlobalObject* globalObject, CallFrame* callFrame))
 687  {
 688      VM& vm = globalObject->vm();
 689      auto scope = DECLARE_THROW_SCOPE(vm);
 690      JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict());
 691  
 692      JSObject* thisObject = thisValue.toObject(globalObject);
 693      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 694      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObject));
 695      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 696      if (length > std::numeric_limits<unsigned>::max()) {
 697          throwOutOfMemoryError(globalObject, scope);
 698          return encodedJSValue();
 699      }
 700  
 701      StringRecursionChecker checker(globalObject, thisObject);
 702      EXCEPTION_ASSERT(!scope.exception() || checker.earlyReturnValue());
 703      if (JSValue earlyReturnValue = checker.earlyReturnValue())
 704          return JSValue::encode(earlyReturnValue);
 705  
 706      JSStringJoiner stringJoiner(globalObject, ',', static_cast<uint32_t>(length));
 707      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 708  
 709      ArgList arguments(callFrame);
 710      for (unsigned i = 0; i < length; ++i) {
 711          JSValue element = thisObject->getIndex(globalObject, i);
 712          RETURN_IF_EXCEPTION(scope, encodedJSValue());
 713          if (element.isUndefinedOrNull())
 714              element = jsEmptyString(vm);
 715          else {
 716              JSValue conversionFunction = element.get(globalObject, vm.propertyNames->toLocaleString);
 717              RETURN_IF_EXCEPTION(scope, encodedJSValue());
 718              auto callData = getCallData(vm, conversionFunction);
 719              if (callData.type != CallData::Type::None) {
 720                  element = call(globalObject, conversionFunction, callData, element, arguments);
 721                  RETURN_IF_EXCEPTION(scope, encodedJSValue());
 722              }
 723          }
 724          stringJoiner.append(globalObject, element);
 725          RETURN_IF_EXCEPTION(scope, encodedJSValue());
 726      }
 727  
 728      RELEASE_AND_RETURN(scope, JSValue::encode(stringJoiner.join(globalObject)));
 729  }
 730  
 731  static JSValue slowJoin(JSGlobalObject* globalObject, JSObject* thisObject, JSString* separator, uint64_t length)
 732  {
 733      VM& vm = globalObject->vm();
 734      auto scope = DECLARE_THROW_SCOPE(vm);
 735  
 736      // 5. If len is zero, return the empty String.
 737      if (!length)
 738          return jsEmptyString(vm);
 739  
 740      // 6. Let element0 be Get(O, "0").
 741      JSValue element0 = thisObject->getIndex(globalObject, 0);
 742      RETURN_IF_EXCEPTION(scope, { });
 743  
 744      // 7. If element0 is undefined or null, let R be the empty String; otherwise, let R be ? ToString(element0).
 745      JSString* r = nullptr;
 746      if (element0.isUndefinedOrNull())
 747          r = jsEmptyString(vm);
 748      else
 749          r = element0.toString(globalObject);
 750      RETURN_IF_EXCEPTION(scope, { });
 751  
 752      // 8. Let k be 1.
 753      // 9. Repeat, while k < len
 754      // 9.e Increase k by 1..
 755      for (uint64_t k = 1; k < length; ++k) {
 756          // b. Let element be ? Get(O, ! ToString(k)).
 757          JSValue element = thisObject->get(globalObject, Identifier::fromString(vm, AtomString::number(k)));
 758          RETURN_IF_EXCEPTION(scope, { });
 759  
 760          // c. If element is undefined or null, let next be the empty String; otherwise, let next be ? ToString(element).
 761          JSString* next = nullptr;
 762          if (element.isUndefinedOrNull()) {
 763              if (!separator->length())
 764                  continue;
 765              next = jsEmptyString(vm);
 766          } else
 767              next = element.toString(globalObject);
 768          RETURN_IF_EXCEPTION(scope, { });
 769  
 770          // a. Let S be the String value produced by concatenating R and sep.
 771          // d. Let R be a String value produced by concatenating S and next.
 772          r = jsString(globalObject, r, separator, next);
 773          RETURN_IF_EXCEPTION(scope, { });
 774      }
 775      // 10. Return R.
 776      return r;
 777  }
 778  
 779  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncJoin, (JSGlobalObject* globalObject, CallFrame* callFrame))
 780  {
 781      VM& vm = globalObject->vm();
 782      auto scope = DECLARE_THROW_SCOPE(vm);
 783  
 784      // 1. Let O be ? ToObject(this value).
 785      JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
 786      EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
 787      if (UNLIKELY(!thisObject))
 788          return encodedJSValue();
 789  
 790      StringRecursionChecker checker(globalObject, thisObject);
 791      EXCEPTION_ASSERT(!scope.exception() || checker.earlyReturnValue());
 792      if (JSValue earlyReturnValue = checker.earlyReturnValue())
 793          return JSValue::encode(earlyReturnValue);
 794  
 795      // 2. Let len be ? ToLength(? Get(O, "length")).
 796      double length = toLength(globalObject, thisObject);
 797      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 798  
 799      // 3. If separator is undefined, let separator be the single-element String ",".
 800      JSValue separatorValue = callFrame->argument(0);
 801      if (separatorValue.isUndefined()) {
 802          const LChar comma = ',';
 803  
 804          if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
 805              uint64_t length64 = static_cast<uint64_t>(length);
 806              ASSERT(static_cast<double>(length64) == length);
 807              JSString* jsSeparator = jsSingleCharacterString(vm, comma);
 808              RETURN_IF_EXCEPTION(scope, encodedJSValue());
 809  
 810              RELEASE_AND_RETURN(scope, JSValue::encode(slowJoin(globalObject, thisObject, jsSeparator, length64)));
 811          }
 812  
 813          unsigned unsignedLength = static_cast<unsigned>(length);
 814          ASSERT(static_cast<double>(unsignedLength) == length);
 815  
 816          RELEASE_AND_RETURN(scope, JSValue::encode(fastJoin(globalObject, thisObject, { &comma, 1 }, unsignedLength)));
 817      }
 818  
 819      // 4. Let sep be ? ToString(separator).
 820      JSString* jsSeparator = separatorValue.toString(globalObject);
 821      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 822  
 823      if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
 824          uint64_t length64 = static_cast<uint64_t>(length);
 825          ASSERT(static_cast<double>(length64) == length);
 826  
 827          RELEASE_AND_RETURN(scope, JSValue::encode(slowJoin(globalObject, thisObject, jsSeparator, length64)));
 828      }
 829  
 830      auto viewWithString = jsSeparator->viewWithUnderlyingString(globalObject);
 831      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 832  
 833      RELEASE_AND_RETURN(scope, JSValue::encode(fastJoin(globalObject, thisObject, viewWithString.view, length)));
 834  }
 835  
 836  inline EncodedJSValue createArrayIteratorObject(JSGlobalObject* globalObject, CallFrame* callFrame, IterationKind kind)
 837  {
 838      VM& vm = globalObject->vm();
 839      auto scope = DECLARE_THROW_SCOPE(vm);
 840  
 841      JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
 842      EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
 843      UNUSED_PARAM(scope);
 844      if (UNLIKELY(!thisObject))
 845          return encodedJSValue();
 846  
 847      return JSValue::encode(JSArrayIterator::create(vm, globalObject->arrayIteratorStructure(), thisObject, jsNumber(static_cast<unsigned>(kind))));
 848  }
 849  
 850  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncValues, (JSGlobalObject* globalObject, CallFrame* callFrame))
 851  {
 852      return createArrayIteratorObject(globalObject, callFrame, IterationKind::Values);
 853  }
 854  
 855  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncEntries, (JSGlobalObject* globalObject, CallFrame* callFrame))
 856  {
 857      return createArrayIteratorObject(globalObject, callFrame, IterationKind::Entries);
 858  }
 859  
 860  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncKeys, (JSGlobalObject* globalObject, CallFrame* callFrame))
 861  {
 862      return createArrayIteratorObject(globalObject, callFrame, IterationKind::Keys);
 863  }
 864  
 865  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncPop, (JSGlobalObject* globalObject, CallFrame* callFrame))
 866  {
 867      VM& vm = globalObject->vm();
 868      auto scope = DECLARE_THROW_SCOPE(vm);
 869  
 870      JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict());
 871  
 872      if (isJSArray(thisValue))
 873          RELEASE_AND_RETURN(scope, JSValue::encode(asArray(thisValue)->pop(globalObject)));
 874  
 875      JSObject* thisObj = thisValue.toObject(globalObject);
 876      EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
 877      if (UNLIKELY(!thisObj))
 878          return encodedJSValue();
 879      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObj));
 880      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 881  
 882      if (length == 0) {
 883          scope.release();
 884          setLength(globalObject, vm, thisObj, length);
 885          return JSValue::encode(jsUndefined());
 886      }
 887  
 888      static_assert(MAX_ARRAY_INDEX + 1 > MAX_ARRAY_INDEX);
 889      uint64_t index = length - 1;
 890      JSValue result = thisObj->get(globalObject, index);
 891      RETURN_IF_EXCEPTION(scope, { });
 892      bool success = thisObj->deleteProperty(globalObject, index);
 893      RETURN_IF_EXCEPTION(scope, { });
 894      if (UNLIKELY(!success)) {
 895          throwTypeError(globalObject, scope, UnableToDeletePropertyError);
 896          return { };
 897      }
 898  
 899      scope.release();
 900      setLength(globalObject, vm, thisObj, index);
 901      return JSValue::encode(result);
 902  }
 903  
 904  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncPush, (JSGlobalObject* globalObject, CallFrame* callFrame))
 905  {
 906      VM& vm = globalObject->vm();
 907      auto scope = DECLARE_THROW_SCOPE(vm);
 908      JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict());
 909  
 910      if (LIKELY(isJSArray(thisValue) && callFrame->argumentCount() == 1)) {
 911          JSArray* array = asArray(thisValue);
 912          scope.release();
 913          array->pushInline(globalObject, callFrame->uncheckedArgument(0));
 914          return JSValue::encode(jsNumber(array->length()));
 915      }
 916      
 917      JSObject* thisObj = thisValue.toObject(globalObject);
 918      EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
 919      if (UNLIKELY(!thisObj))
 920          return encodedJSValue();
 921      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObj));
 922      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 923      unsigned argCount = callFrame->argumentCount();
 924  
 925      if (UNLIKELY(length + argCount > static_cast<uint64_t>(maxSafeInteger())))
 926          return throwVMTypeError(globalObject, scope, "push cannot produce an array of length larger than (2 ** 53) - 1"_s);
 927  
 928      for (unsigned n = 0; n < argCount; n++) {
 929          thisObj->putByIndexInline(globalObject, length + n, callFrame->uncheckedArgument(n), true);
 930          RETURN_IF_EXCEPTION(scope, { });
 931      }
 932      
 933      uint64_t newLength = length + argCount;
 934      scope.release();
 935      setLength(globalObject, vm, thisObj, newLength);
 936      return JSValue::encode(jsNumber(newLength));
 937  }
 938  
 939  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncReverse, (JSGlobalObject* globalObject, CallFrame* callFrame))
 940  {
 941      VM& vm = globalObject->vm();
 942      auto scope = DECLARE_THROW_SCOPE(vm);
 943  
 944      JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
 945      EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
 946      if (UNLIKELY(!thisObject))
 947          return encodedJSValue();
 948  
 949      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObject));
 950      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 951  
 952      thisObject->ensureWritable(vm);
 953  
 954      switch (thisObject->indexingType()) {
 955      case ALL_CONTIGUOUS_INDEXING_TYPES:
 956      case ALL_INT32_INDEXING_TYPES: {
 957          auto& butterfly = *thisObject->butterfly();
 958          if (length > butterfly.publicLength())
 959              break;
 960          auto data = butterfly.contiguous().data();
 961          if (containsHole(data, static_cast<uint32_t>(length)) && holesMustForwardToPrototype(vm, thisObject))
 962              break;
 963          std::reverse(data, data + length);
 964          if (!hasInt32(thisObject->indexingType()))
 965              vm.heap.writeBarrier(thisObject);
 966          return JSValue::encode(thisObject);
 967      }
 968      case ALL_DOUBLE_INDEXING_TYPES: {
 969          auto& butterfly = *thisObject->butterfly();
 970          if (length > butterfly.publicLength())
 971              break;
 972          auto data = butterfly.contiguousDouble().data();
 973          if (containsHole(data, static_cast<uint32_t>(length)) && holesMustForwardToPrototype(vm, thisObject))
 974              break;
 975          std::reverse(data, data + length);
 976          return JSValue::encode(thisObject);
 977      }
 978      case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
 979          auto& storage = *thisObject->butterfly()->arrayStorage();
 980          if (length > storage.vectorLength())
 981              break;
 982          if (storage.hasHoles() && holesMustForwardToPrototype(vm, thisObject))
 983              break;
 984          auto data = storage.vector().data();
 985          std::reverse(data, data + length);
 986          vm.heap.writeBarrier(thisObject);
 987          return JSValue::encode(thisObject);
 988      }
 989      }
 990  
 991      uint64_t middle = length / 2;
 992      for (uint64_t lower = 0; lower < middle; lower++) {
 993          uint64_t upper = length - lower - 1;
 994          bool lowerExists = thisObject->hasProperty(globalObject, lower);
 995          RETURN_IF_EXCEPTION(scope, encodedJSValue());
 996          JSValue lowerValue;
 997          if (lowerExists) {
 998              lowerValue = thisObject->get(globalObject, lower);
 999              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1000          }
1001  
1002          bool upperExists = thisObject->hasProperty(globalObject, upper);
1003          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1004          JSValue upperValue;
1005          if (upperExists) {
1006              upperValue = thisObject->get(globalObject, upper);
1007              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1008          }
1009  
1010          if (!lowerExists && !upperExists) {
1011              // Spec says to do nothing when neither lower nor upper exist.
1012              continue;
1013          }
1014  
1015          if (upperExists) {
1016              thisObject->putByIndexInline(globalObject, lower, upperValue, true);
1017              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1018          } else {
1019              bool success = thisObject->deleteProperty(globalObject, lower);
1020              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1021              if (UNLIKELY(!success)) {
1022                  throwTypeError(globalObject, scope, UnableToDeletePropertyError);
1023                  return encodedJSValue();
1024              }
1025          }
1026  
1027          if (lowerExists) {
1028              thisObject->putByIndexInline(globalObject, upper, lowerValue, true);
1029              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1030          } else {
1031              bool success = thisObject->deleteProperty(globalObject, upper);
1032              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1033              if (UNLIKELY(!success)) {
1034                  throwTypeError(globalObject, scope, UnableToDeletePropertyError);
1035                  return encodedJSValue();
1036              }
1037          }
1038      }
1039      return JSValue::encode(thisObject);
1040  }
1041  
1042  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncShift, (JSGlobalObject* globalObject, CallFrame* callFrame))
1043  {
1044      VM& vm = globalObject->vm();
1045      auto scope = DECLARE_THROW_SCOPE(vm);
1046      JSObject* thisObj = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
1047      EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1048      if (UNLIKELY(!thisObj))
1049          return encodedJSValue();
1050      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObj));
1051      RETURN_IF_EXCEPTION(scope, encodedJSValue());
1052  
1053      if (length == 0) {
1054          scope.release();
1055          setLength(globalObject, vm, thisObj, length);
1056          return JSValue::encode(jsUndefined());
1057      }
1058  
1059      JSValue result = thisObj->getIndex(globalObject, 0);
1060      RETURN_IF_EXCEPTION(scope, encodedJSValue());
1061      shift<JSArray::ShiftCountForShift>(globalObject, thisObj, 0, 1, 0, length);
1062      RETURN_IF_EXCEPTION(scope, encodedJSValue());
1063      scope.release();
1064      setLength(globalObject, vm, thisObj, length - 1);
1065      return JSValue::encode(result);
1066  }
1067  
1068  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncSlice, (JSGlobalObject* globalObject, CallFrame* callFrame))
1069  {
1070      // https://tc39.github.io/ecma262/#sec-array.prototype.slice
1071      VM& vm = globalObject->vm();
1072      auto scope = DECLARE_THROW_SCOPE(vm);
1073      JSObject* thisObj = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
1074      EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1075      if (UNLIKELY(!thisObj))
1076          return { };
1077      uint64_t length = toLength(globalObject, thisObj);
1078      RETURN_IF_EXCEPTION(scope, { });
1079  
1080      uint64_t begin = argumentClampedIndexFromStartOrEnd(globalObject, callFrame->argument(0), length);
1081      RETURN_IF_EXCEPTION(scope, { });
1082      uint64_t end = argumentClampedIndexFromStartOrEnd(globalObject, callFrame->argument(1), length, length);
1083      RETURN_IF_EXCEPTION(scope, { });
1084      if (end < begin)
1085          end = begin;
1086  
1087      std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(globalObject, thisObj, end - begin);
1088      // We can only get an exception if we call some user function.
1089      EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1090      if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
1091          return { };
1092  
1093      bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == toLength(globalObject, thisObj);
1094      RETURN_IF_EXCEPTION(scope, { });
1095      if (LIKELY(okToDoFastPath)) {
1096          if (JSArray* result = asArray(thisObj)->fastSlice(globalObject, static_cast<uint32_t>(begin), static_cast<uint32_t>(end - begin)))
1097              return JSValue::encode(result);
1098      }
1099  
1100      JSObject* result;
1101      if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1102          result = speciesResult.second;
1103      else {
1104          if (UNLIKELY(end - begin > std::numeric_limits<uint32_t>::max())) {
1105              throwRangeError(globalObject, scope, LengthExceededTheMaximumArrayLengthError);
1106              return encodedJSValue();
1107          }
1108          result = constructEmptyArray(globalObject, nullptr, static_cast<uint32_t>(end - begin));
1109          RETURN_IF_EXCEPTION(scope, { });
1110      }
1111  
1112      // Document that we need to keep the source array alive until after anything
1113      // that can GC (e.g. allocating the result array).
1114      thisObj->use();
1115  
1116      uint64_t n = 0;
1117      for (uint64_t k = begin; k < end; k++, n++) {
1118          JSValue v = getProperty(globalObject, thisObj, k);
1119          RETURN_IF_EXCEPTION(scope, { });
1120          if (v) {
1121              result->putDirectIndex(globalObject, n, v, 0, PutDirectIndexShouldThrow);
1122              RETURN_IF_EXCEPTION(scope, { });
1123          }
1124      }
1125      scope.release();
1126      setLength(globalObject, vm, result, n);
1127      return JSValue::encode(result);
1128  }
1129  
1130  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncSplice, (JSGlobalObject* globalObject, CallFrame* callFrame))
1131  {
1132      // 15.4.4.12
1133  
1134      VM& vm = globalObject->vm();
1135      auto scope = DECLARE_THROW_SCOPE(vm);
1136  
1137      JSObject* thisObj = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
1138      EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1139      if (UNLIKELY(!thisObj))
1140          return encodedJSValue();
1141      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObj));
1142      RETURN_IF_EXCEPTION(scope, encodedJSValue());
1143  
1144      if (!callFrame->argumentCount()) {
1145          std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(globalObject, thisObj, 0);
1146          EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1147          if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
1148              return encodedJSValue();
1149  
1150          JSObject* result;
1151          if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1152              result = speciesResult.second;
1153          else {
1154              result = constructEmptyArray(globalObject, nullptr);
1155              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1156          }
1157  
1158          setLength(globalObject, vm, result, 0);
1159          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1160          scope.release();
1161          setLength(globalObject, vm, thisObj, length);
1162          return JSValue::encode(result);
1163      }
1164  
1165      uint64_t actualStart = argumentClampedIndexFromStartOrEnd(globalObject, callFrame->argument(0), length);
1166      RETURN_IF_EXCEPTION(scope, encodedJSValue());
1167  
1168      uint64_t actualDeleteCount = length - actualStart;
1169      if (callFrame->argumentCount() > 1) {
1170          double deleteCount = callFrame->uncheckedArgument(1).toInteger(globalObject);
1171          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1172          if (deleteCount < 0)
1173              actualDeleteCount = 0;
1174          else if (deleteCount > length - actualStart)
1175              actualDeleteCount = length - actualStart;
1176          else
1177              actualDeleteCount = static_cast<uint64_t>(deleteCount);
1178      }
1179      unsigned itemCount = std::max<int>(callFrame->argumentCount() - 2, 0);
1180      if (UNLIKELY(length - actualDeleteCount + itemCount > static_cast<uint64_t>(maxSafeInteger())))
1181          return throwVMTypeError(globalObject, scope, "Splice cannot produce an array of length larger than (2 ** 53) - 1"_s);
1182  
1183      std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(globalObject, thisObj, actualDeleteCount);
1184      EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1185      if (speciesResult.first == SpeciesConstructResult::Exception)
1186          return JSValue::encode(jsUndefined());
1187  
1188      JSObject* result = nullptr;
1189      bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == toLength(globalObject, thisObj);
1190      RETURN_IF_EXCEPTION(scope, encodedJSValue());
1191      if (LIKELY(okToDoFastPath))
1192          result = asArray(thisObj)->fastSlice(globalObject, static_cast<uint32_t>(actualStart), static_cast<uint32_t>(actualDeleteCount));
1193  
1194      if (!result) {
1195          if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1196              result = speciesResult.second;
1197          else {
1198              if (UNLIKELY(actualDeleteCount > std::numeric_limits<uint32_t>::max())) {
1199                  throwRangeError(globalObject, scope, LengthExceededTheMaximumArrayLengthError);
1200                  return encodedJSValue();
1201              }
1202              result = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), static_cast<uint32_t>(actualDeleteCount));
1203              if (UNLIKELY(!result)) {
1204                  throwOutOfMemoryError(globalObject, scope);
1205                  return encodedJSValue();
1206              }
1207          }
1208          for (uint64_t k = 0; k < actualDeleteCount; ++k) {
1209              JSValue v = getProperty(globalObject, thisObj, k + actualStart);
1210              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1211              if (UNLIKELY(!v))
1212                  continue;
1213              result->putDirectIndex(globalObject, k, v, 0, PutDirectIndexShouldThrow);
1214              RETURN_IF_EXCEPTION(scope, encodedJSValue());
1215          }
1216          setLength(globalObject, vm, result, actualDeleteCount);
1217          RETURN_IF_EXCEPTION(scope, { });
1218      }
1219  
1220      if (itemCount < actualDeleteCount) {
1221          shift<JSArray::ShiftCountForSplice>(globalObject, thisObj, actualStart, actualDeleteCount, itemCount, length);
1222          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1223      } else if (itemCount > actualDeleteCount) {
1224          unshift<JSArray::ShiftCountForSplice>(globalObject, thisObj, actualStart, actualDeleteCount, itemCount, length);
1225          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1226      }
1227      for (unsigned k = 0; k < itemCount; ++k) {
1228          thisObj->putByIndexInline(globalObject, k + actualStart, callFrame->uncheckedArgument(k + 2), true);
1229          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1230      }
1231      
1232      scope.release();
1233      setLength(globalObject, vm, thisObj, length - actualDeleteCount + itemCount);
1234      return JSValue::encode(result);
1235  }
1236  
1237  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncUnShift, (JSGlobalObject* globalObject, CallFrame* callFrame))
1238  {
1239      VM& vm = globalObject->vm();
1240      auto scope = DECLARE_THROW_SCOPE(vm);
1241      // 15.4.4.13
1242  
1243      JSObject* thisObj = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
1244      EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1245      if (UNLIKELY(!thisObj))
1246          return encodedJSValue();
1247      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObj));
1248      RETURN_IF_EXCEPTION(scope, encodedJSValue());
1249  
1250      unsigned nrArgs = callFrame->argumentCount();
1251      if (nrArgs) {
1252          if (UNLIKELY(length + nrArgs > static_cast<uint64_t>(maxSafeInteger())))
1253              return throwVMTypeError(globalObject, scope, "unshift cannot produce an array of length larger than (2 ** 53) - 1"_s);
1254          unshift<JSArray::ShiftCountForShift>(globalObject, thisObj, 0, 0, nrArgs, length);
1255          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1256      }
1257      for (unsigned k = 0; k < nrArgs; ++k) {
1258          thisObj->putByIndexInline(globalObject, k, callFrame->uncheckedArgument(k), true);
1259          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1260      }
1261      uint64_t newLength = length + nrArgs;
1262      scope.release();
1263      setLength(globalObject, vm, thisObj, newLength);
1264      return JSValue::encode(jsNumber(newLength));
1265  }
1266  
1267  enum class IndexOfDirection { Forward, Backward };
1268  template<IndexOfDirection direction>
1269  ALWAYS_INLINE JSValue fastIndexOf(JSGlobalObject* globalObject, VM& vm, JSArray* array, uint64_t length64, JSValue searchElement, uint64_t index64)
1270  {
1271      auto scope = DECLARE_THROW_SCOPE(vm);
1272  
1273      bool canDoFastPath = array->canDoFastIndexedAccess(vm)
1274          && array->getArrayLength() == length64 // The effects in getting `index` could have changed the length of this array.
1275          && static_cast<uint32_t>(index64) == index64;
1276      if (!canDoFastPath)
1277          return JSValue();
1278  
1279      uint32_t length = static_cast<uint32_t>(length64);
1280      uint32_t index = static_cast<uint32_t>(index64);
1281  
1282      switch (array->indexingType()) {
1283      case ALL_INT32_INDEXING_TYPES: {
1284          if (!searchElement.isNumber())
1285              return jsNumber(-1);
1286          JSValue searchInt32;
1287          if (searchElement.isInt32())
1288              searchInt32 = searchElement;
1289          else {
1290              double searchNumber = searchElement.asNumber();
1291              if (!canBeInt32(searchNumber))
1292                  return jsNumber(-1);
1293              searchInt32 = jsNumber(static_cast<int32_t>(searchNumber));
1294          }
1295          auto& butterfly = *array->butterfly();
1296          auto data = butterfly.contiguous().data();
1297          if (direction == IndexOfDirection::Forward) {
1298              for (; index < length; ++index) {
1299                  // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
1300                  // And the hole never matches against Int32 value.
1301                  if (searchInt32 == data[index].get())
1302                      return jsNumber(index);
1303              }
1304          } else {
1305              do {
1306                  ASSERT(index < length);
1307                  // Array#lastIndexOf uses `===` semantics (not HashMap isEqual semantics).
1308                  // And the hole never matches against Int32 value.
1309                  if (searchInt32 == data[index].get())
1310                      return jsNumber(index);
1311              } while (index--);
1312          }
1313          return jsNumber(-1);
1314      }
1315      case ALL_CONTIGUOUS_INDEXING_TYPES: {
1316          auto& butterfly = *array->butterfly();
1317          auto data = butterfly.contiguous().data();
1318  
1319          if (direction == IndexOfDirection::Forward) {
1320              for (; index < length; ++index) {
1321                  JSValue value = data[index].get();
1322                  if (!value)
1323                      continue;
1324                  bool isEqual = JSValue::strictEqual(globalObject, searchElement, value);
1325                  RETURN_IF_EXCEPTION(scope, { });
1326                  if (isEqual)
1327                      return jsNumber(index);
1328              }
1329          } else {
1330              do {
1331                  ASSERT(index < length);
1332                  JSValue value = data[index].get();
1333                  if (!value)
1334                      continue;
1335                  bool isEqual = JSValue::strictEqual(globalObject, searchElement, value);
1336                  RETURN_IF_EXCEPTION(scope, { });
1337                  if (isEqual)
1338                      return jsNumber(index);
1339              } while (index--);
1340          }
1341          return jsNumber(-1);
1342      }
1343      case ALL_DOUBLE_INDEXING_TYPES: {
1344          if (!searchElement.isNumber())
1345              return jsNumber(-1);
1346          double searchNumber = searchElement.asNumber();
1347          auto& butterfly = *array->butterfly();
1348          auto data = butterfly.contiguousDouble().data();
1349          if (direction == IndexOfDirection::Forward) {
1350              for (; index < length; ++index) {
1351                  // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
1352                  // And the hole never matches since it is NaN.
1353                  if (data[index] == searchNumber)
1354                      return jsNumber(index);
1355              }
1356          } else {
1357              do {
1358                  ASSERT(index < length);
1359                  // Array#lastIndexOf uses `===` semantics (not HashMap isEqual semantics).
1360                  // And the hole never matches since it is NaN.
1361                  if (data[index] == searchNumber)
1362                      return jsNumber(index);
1363              } while (index--);
1364          }
1365          return jsNumber(-1);
1366      }
1367      default:
1368          return JSValue();
1369      }
1370  }
1371  
1372  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncIndexOf, (JSGlobalObject* globalObject, CallFrame* callFrame))
1373  {
1374      VM& vm = globalObject->vm();
1375      auto scope = DECLARE_THROW_SCOPE(vm);
1376  
1377      // 15.4.4.14
1378      JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
1379      EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
1380      if (UNLIKELY(!thisObject))
1381          return { };
1382      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObject));
1383      RETURN_IF_EXCEPTION(scope, { });
1384      if (!length)
1385          return JSValue::encode(jsNumber(-1));
1386  
1387      uint64_t index = argumentClampedIndexFromStartOrEnd(globalObject, callFrame->argument(1), length);
1388      RETURN_IF_EXCEPTION(scope, { });
1389      JSValue searchElement = callFrame->argument(0);
1390  
1391      if (isJSArray(thisObject)) {
1392          JSValue result = fastIndexOf<IndexOfDirection::Forward>(globalObject, vm, asArray(thisObject), length, searchElement, index);
1393          RETURN_IF_EXCEPTION(scope, { });
1394          if (result)
1395              return JSValue::encode(result);
1396      }
1397  
1398      for (; index < length; ++index) {
1399          JSValue e = getProperty(globalObject, thisObject, index);
1400          RETURN_IF_EXCEPTION(scope, { });
1401          if (!e)
1402              continue;
1403          bool isEqual = JSValue::strictEqual(globalObject, searchElement, e);
1404          RETURN_IF_EXCEPTION(scope, { });
1405          if (isEqual)
1406              return JSValue::encode(jsNumber(index));
1407      }
1408  
1409      return JSValue::encode(jsNumber(-1));
1410  }
1411  
1412  JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncLastIndexOf, (JSGlobalObject* globalObject, CallFrame* callFrame))
1413  {
1414      VM& vm = globalObject->vm();
1415      auto scope = DECLARE_THROW_SCOPE(vm);
1416  
1417      // 15.4.4.15
1418      JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject);
1419      EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
1420      if (UNLIKELY(!thisObject))
1421          return { };
1422      uint64_t length = static_cast<uint64_t>(toLength(globalObject, thisObject));
1423      RETURN_IF_EXCEPTION(scope, { });
1424      if (!length)
1425          return JSValue::encode(jsNumber(-1));
1426  
1427      uint64_t index = length - 1;
1428      if (callFrame->argumentCount() >= 2) {
1429          JSValue fromValue = callFrame->uncheckedArgument(1);
1430          double fromDouble = fromValue.toInteger(globalObject);
1431          RETURN_IF_EXCEPTION(scope, { });
1432          if (fromDouble < 0) {
1433              fromDouble += length;
1434              if (fromDouble < 0)
1435                  return JSValue::encode(jsNumber(-1));
1436          }
1437          if (fromDouble < length)
1438              index = static_cast<uint64_t>(fromDouble);
1439      }
1440  
1441      JSValue searchElement = callFrame->argument(0);
1442  
1443      if (isJSArray(thisObject)) {
1444          JSValue result = fastIndexOf<IndexOfDirection::Backward>(globalObject, vm, asArray(thisObject), length, searchElement, index);
1445          RETURN_IF_EXCEPTION(scope, { });
1446          if (result)
1447              return JSValue::encode(result);
1448      }
1449  
1450      do {
1451          ASSERT(index < length);
1452          JSValue e = getProperty(globalObject, thisObject, index);
1453          RETURN_IF_EXCEPTION(scope, { });
1454          if (!e)
1455              continue;
1456          bool isEqual = JSValue::strictEqual(globalObject, searchElement, e);
1457          RETURN_IF_EXCEPTION(scope, { });
1458          if (isEqual)
1459              return JSValue::encode(jsNumber(index));
1460      } while (index--);
1461  
1462      return JSValue::encode(jsNumber(-1));
1463  }
1464  
1465  static bool moveElements(JSGlobalObject* globalObject, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
1466  {
1467      auto scope = DECLARE_THROW_SCOPE(vm);
1468  
1469      if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !holesMustForwardToPrototype(vm, source))) {
1470          for (unsigned i = 0; i < sourceLength; ++i) {
1471              JSValue value = source->tryGetIndexQuickly(i);
1472              if (value) {
1473                  target->putDirectIndex(globalObject, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1474                  RETURN_IF_EXCEPTION(scope, false);
1475              }
1476          }
1477      } else {
1478          for (unsigned i = 0; i < sourceLength; ++i) {
1479              JSValue value = getProperty(globalObject, source, i);
1480              RETURN_IF_EXCEPTION(scope, false);
1481              if (value) {
1482                  target->putDirectIndex(globalObject, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1483                  RETURN_IF_EXCEPTION(scope, false);
1484              }
1485          }
1486      }
1487      return true;
1488  }
1489  
1490  static EncodedJSValue concatAppendOne(JSGlobalObject* globalObject, VM& vm, JSArray* first, JSValue second)
1491  {
1492      auto scope = DECLARE_THROW_SCOPE(vm);
1493  
1494      ASSERT(!isJSArray(second));
1495      ASSERT(!shouldUseSlowPut(first->indexingType()));
1496      Butterfly* firstButterfly = first->butterfly();
1497      unsigned firstArraySize = firstButterfly->publicLength();
1498  
1499      Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1500      checkedResultSize += 1;
1501      if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1502          throwOutOfMemoryError(globalObject, scope);
1503          return encodedJSValue();
1504      }
1505  
1506      unsigned resultSize = checkedResultSize.unsafeGet();
1507      IndexingType type = first->mergeIndexingTypeForCopying(indexingTypeForValue(second) | IsArray);
1508      
1509      if (type == NonArray)
1510          type = first->indexingType();
1511  
1512      Structure* resultStructure = globalObject->arrayStructureForIndexingTypeDuringAllocation(type);
1513      JSArray* result = JSArray::tryCreate(vm, resultStructure, resultSize);
1514      if (UNLIKELY(!result)) {
1515          throwOutOfMemoryError(globalObject, scope);
1516          return encodedJSValue();
1517      }
1518  
1519      bool success = result->appendMemcpy(globalObject, vm, 0, first);
1520      EXCEPTION_ASSERT(!scope.exception() || !success);
1521      if (!success) {
1522          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1523  
1524          bool success = moveElements(globalObject, vm, result, 0, first, firstArraySize);
1525          EXCEPTION_ASSERT(!scope.exception() == success);
1526          if (UNLIKELY(!success))
1527              return encodedJSValue();
1528      }
1529  
1530      scope.release();
1531      result->putDirectIndex(globalObject, firstArraySize, second);
1532      return JSValue::encode(result);
1533  
1534  }
1535  
1536  template<typename T>
1537  void clearElement(T& element)
1538  {
1539      element.clear();
1540  }
1541  
1542  template<>
1543  void clearElement(double& element)
1544  {
1545      element = PNaN;
1546  }
1547  
1548  template<typename T>
1549  ALWAYS_INLINE void copyElements(T* buffer, unsigned offset, T* source, unsigned sourceSize, IndexingType sourceType)
1550  {
1551      if (sourceType != ArrayWithUndecided) {
1552          gcSafeMemcpy(buffer + offset, source, sizeof(JSValue) * sourceSize);
1553          return;
1554      }
1555  
1556      for (unsigned i = sourceSize; i--;)
1557          clearElement<T>(buffer[i + offset]);
1558  };
1559  
1560  JSC_DEFINE_HOST_FUNCTION(arrayProtoPrivateFuncConcatMemcpy, (JSGlobalObject* globalObject, CallFrame* callFrame))
1561  {
1562      ASSERT(callFrame->argumentCount() == 2);
1563      VM& vm = globalObject->vm();
1564      auto scope = DECLARE_THROW_SCOPE(vm);
1565  
1566      JSArray* firstArray = jsCast<JSArray*>(callFrame->uncheckedArgument(0));
1567      
1568      // This code assumes that neither array has set Symbol.isConcatSpreadable. If the first array
1569      // has indexed accessors then one of those accessors might change the value of Symbol.isConcatSpreadable
1570      // on the second argument.
1571      if (UNLIKELY(shouldUseSlowPut(firstArray->indexingType())))
1572          return JSValue::encode(jsNull());
1573  
1574      // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
1575      bool isValid = speciesWatchpointIsValid(vm, firstArray);
1576      scope.assertNoException();
1577      if (UNLIKELY(!isValid))
1578          return JSValue::encode(jsNull());
1579  
1580      JSValue second = callFrame->uncheckedArgument(1);
1581      if (!isJSArray(second))
1582          RELEASE_AND_RETURN(scope, concatAppendOne(globalObject, vm, firstArray, second));
1583  
1584      JSArray* secondArray = jsCast<JSArray*>(second);
1585      
1586      Butterfly* firstButterfly = firstArray->butterfly();
1587      Butterfly* secondButterfly = secondArray->butterfly();
1588  
1589      unsigned firstArraySize = firstButterfly->publicLength();
1590      unsigned secondArraySize = secondButterfly->publicLength();
1591  
1592      Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1593      checkedResultSize += secondArraySize;
1594  
1595      if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1596          throwOutOfMemoryError(globalObject, scope);
1597          return encodedJSValue();
1598      }
1599  
1600      unsigned resultSize = checkedResultSize.unsafeGet();
1601      IndexingType firstType = firstArray->indexingType();
1602      IndexingType secondType = secondArray->indexingType();
1603      IndexingType type = firstArray->mergeIndexingTypeForCopying(secondType);
1604      if (type == NonArray || !firstArray->canFastCopy(vm, secondArray) || resultSize >= MIN_SPARSE_ARRAY_INDEX) {
1605          JSArray* result = constructEmptyArray(globalObject, nullptr, resultSize);
1606          RETURN_IF_EXCEPTION(scope, encodedJSValue());
1607  
1608          bool success = moveElements(globalObject, vm, result, 0, firstArray, firstArraySize);
1609          EXCEPTION_ASSERT(!scope.exception() == success);
1610          if (UNLIKELY(!success))
1611              return encodedJSValue();
1612          success = moveElements(globalObject, vm, result, firstArraySize, secondArray, secondArraySize);
1613          EXCEPTION_ASSERT(!scope.exception() == success);
1614          if (UNLIKELY(!success))
1615              return encodedJSValue();
1616  
1617          return JSValue::encode(result);
1618      }
1619  
1620      Structure* resultStructure = globalObject->arrayStructureForIndexingTypeDuringAllocation(type);
1621      if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
1622          return JSValue::encode(jsNull());
1623  
1624      ASSERT(!globalObject->isHavingABadTime());
1625      ObjectInitializationScope initializationScope(vm);
1626      JSArray* result = JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure, resultSize);
1627      if (UNLIKELY(!result)) {
1628          throwOutOfMemoryError(globalObject, scope);
1629          return encodedJSValue();
1630      }
1631  
1632      if (type == ArrayWithDouble) {
1633          double* buffer = result->butterfly()->contiguousDouble().data();
1634          copyElements(buffer, 0, firstButterfly->contiguousDouble().data(), firstArraySize, firstType);
1635          copyElements(buffer, firstArraySize, secondButterfly->contiguousDouble().data(), secondArraySize, secondType);
1636  
1637      } else if (type != ArrayWithUndecided) {
1638          WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1639          copyElements(buffer, 0, firstButterfly->contiguous().data(), firstArraySize, firstType);
1640          copyElements(buffer, firstArraySize, secondButterfly->contiguous().data(), secondArraySize, secondType);
1641      }
1642  
1643      ASSERT(result->butterfly()->publicLength() == resultSize);
1644      return JSValue::encode(result);
1645  }
1646  
1647  JSC_DEFINE_HOST_FUNCTION(arrayProtoPrivateFuncAppendMemcpy, (JSGlobalObject* globalObject, CallFrame* callFrame))
1648  {
1649      ASSERT(callFrame->argumentCount() == 3);
1650  
1651      VM& vm = globalObject->vm();
1652      auto scope = DECLARE_THROW_SCOPE(vm);
1653      JSArray* resultArray = jsCast<JSArray*>(callFrame->uncheckedArgument(0));
1654      JSArray* otherArray = jsCast<JSArray*>(callFrame->uncheckedArgument(1));
1655      JSValue startValue = callFrame->uncheckedArgument(2);
1656      ASSERT(startValue.isUInt32AsAnyInt());
1657      unsigned startIndex = startValue.asUInt32AsAnyInt();
1658      bool success = resultArray->appendMemcpy(globalObject, vm, startIndex, otherArray);
1659      EXCEPTION_ASSERT(!scope.exception() || !success);
1660      if (success)
1661          return JSValue::encode(jsUndefined());
1662      RETURN_IF_EXCEPTION(scope, encodedJSValue());
1663      scope.release();
1664      moveElements(globalObject, vm, resultArray, startIndex, otherArray, otherArray->length());
1665      return JSValue::encode(jsUndefined());
1666  }
1667  
1668  } // namespace JSC