elements.cc 188 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/elements.h"
6

7 8
#include "src/arguments.h"
#include "src/conversions.h"
9
#include "src/frames.h"
10
#include "src/heap/factory.h"
11
#include "src/isolate-inl.h"
12
#include "src/messages.h"
13
#include "src/objects-inl.h"
14
#include "src/objects/arguments-inl.h"
15
#include "src/objects/hash-table-inl.h"
16
#include "src/utils.h"
17 18 19 20 21 22 23 24

// Each concrete ElementsAccessor can handle exactly one ElementsKind,
// several abstract ElementsAccessor classes are used to allow sharing
// common code.
//
// Inheritance hierarchy:
// - ElementsAccessorBase                        (abstract)
//   - FastElementsAccessor                      (abstract)
25 26 27 28 29
//     - FastSmiOrObjectElementsAccessor
//       - FastPackedSmiElementsAccessor
//       - FastHoleySmiElementsAccessor
//       - FastPackedObjectElementsAccessor
//       - FastHoleyObjectElementsAccessor
30
//     - FastDoubleElementsAccessor
31 32
//       - FastPackedDoubleElementsAccessor
//       - FastHoleyDoubleElementsAccessor
33 34 35 36 37 38 39 40 41 42
//   - TypedElementsAccessor: template, with instantiations:
//     - FixedUint8ElementsAccessor
//     - FixedInt8ElementsAccessor
//     - FixedUint16ElementsAccessor
//     - FixedInt16ElementsAccessor
//     - FixedUint32ElementsAccessor
//     - FixedInt32ElementsAccessor
//     - FixedFloat32ElementsAccessor
//     - FixedFloat64ElementsAccessor
//     - FixedUint8ClampedElementsAccessor
43 44
//     - FixedBigUint64ElementsAccessor
//     - FixedBigInt64ElementsAccessor
45
//   - DictionaryElementsAccessor
46
//   - SloppyArgumentsElementsAccessor
47 48
//     - FastSloppyArgumentsElementsAccessor
//     - SlowSloppyArgumentsElementsAccessor
49 50 51
//   - StringWrapperElementsAccessor
//     - FastStringWrapperElementsAccessor
//     - SlowStringWrapperElementsAccessor
52

53 54 55 56
namespace v8 {
namespace internal {


57 58 59
namespace {


60 61
static const int kPackedSizeNotKnown = -1;

cbruni's avatar
cbruni committed
62 63
enum Where { AT_START, AT_END };

64

65 66 67 68 69
// First argument in list is the accessor class, the second argument is the
// accessor ElementsKind, and the third is the backing store class.  Use the
// fast element handler for smi-only arrays.  The implementation is currently
// identical.  Note that the order must match that of the ElementsKind enum for
// the |accessor_array[]| below to work.
70
#define ELEMENTS_LIST(V)                                                      \
71 72 73 74 75
  V(FastPackedSmiElementsAccessor, PACKED_SMI_ELEMENTS, FixedArray)           \
  V(FastHoleySmiElementsAccessor, HOLEY_SMI_ELEMENTS, FixedArray)             \
  V(FastPackedObjectElementsAccessor, PACKED_ELEMENTS, FixedArray)            \
  V(FastHoleyObjectElementsAccessor, HOLEY_ELEMENTS, FixedArray)              \
  V(FastPackedDoubleElementsAccessor, PACKED_DOUBLE_ELEMENTS,                 \
76
    FixedDoubleArray)                                                         \
77
  V(FastHoleyDoubleElementsAccessor, HOLEY_DOUBLE_ELEMENTS, FixedDoubleArray) \
78
  V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, NumberDictionary)        \
79 80 81 82
  V(FastSloppyArgumentsElementsAccessor, FAST_SLOPPY_ARGUMENTS_ELEMENTS,      \
    FixedArray)                                                               \
  V(SlowSloppyArgumentsElementsAccessor, SLOW_SLOPPY_ARGUMENTS_ELEMENTS,      \
    FixedArray)                                                               \
83 84 85 86
  V(FastStringWrapperElementsAccessor, FAST_STRING_WRAPPER_ELEMENTS,          \
    FixedArray)                                                               \
  V(SlowStringWrapperElementsAccessor, SLOW_STRING_WRAPPER_ELEMENTS,          \
    FixedArray)                                                               \
87 88 89 90 91 92 93 94 95
  V(FixedUint8ElementsAccessor, UINT8_ELEMENTS, FixedUint8Array)              \
  V(FixedInt8ElementsAccessor, INT8_ELEMENTS, FixedInt8Array)                 \
  V(FixedUint16ElementsAccessor, UINT16_ELEMENTS, FixedUint16Array)           \
  V(FixedInt16ElementsAccessor, INT16_ELEMENTS, FixedInt16Array)              \
  V(FixedUint32ElementsAccessor, UINT32_ELEMENTS, FixedUint32Array)           \
  V(FixedInt32ElementsAccessor, INT32_ELEMENTS, FixedInt32Array)              \
  V(FixedFloat32ElementsAccessor, FLOAT32_ELEMENTS, FixedFloat32Array)        \
  V(FixedFloat64ElementsAccessor, FLOAT64_ELEMENTS, FixedFloat64Array)        \
  V(FixedUint8ClampedElementsAccessor, UINT8_CLAMPED_ELEMENTS,                \
96 97 98
    FixedUint8ClampedArray)                                                   \
  V(FixedBigUint64ElementsAccessor, BIGUINT64_ELEMENTS, FixedBigUint64Array)  \
  V(FixedBigInt64ElementsAccessor, BIGINT64_ELEMENTS, FixedBigInt64Array)
99 100 101 102 103 104

template<ElementsKind Kind> class ElementsKindTraits {
 public:
  typedef FixedArrayBase BackingStore;
};

105 106 107 108 109 110 111 112
#define ELEMENTS_TRAITS(Class, KindParam, Store)    \
  template <>                                       \
  class ElementsKindTraits<KindParam> {             \
   public: /* NOLINT */                             \
    static constexpr ElementsKind Kind = KindParam; \
    typedef Store BackingStore;                     \
  };                                                \
  constexpr ElementsKind ElementsKindTraits<KindParam>::Kind;
113 114 115
ELEMENTS_LIST(ELEMENTS_TRAITS)
#undef ELEMENTS_TRAITS

116
V8_WARN_UNUSED_RESULT
117
MaybeHandle<Object> ThrowArrayLengthRangeError(Isolate* isolate) {
118
  THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidArrayLength),
119
                  Object);
120 121
}

122 123 124 125 126
WriteBarrierMode GetWriteBarrierMode(ElementsKind kind) {
  if (IsSmiElementsKind(kind)) return SKIP_WRITE_BARRIER;
  if (IsDoubleElementsKind(kind)) return SKIP_WRITE_BARRIER;
  return UPDATE_WRITE_BARRIER;
}
127

128
void CopyObjectToObjectElements(Isolate* isolate, FixedArrayBase* from_base,
129 130 131
                                ElementsKind from_kind, uint32_t from_start,
                                FixedArrayBase* to_base, ElementsKind to_kind,
                                uint32_t to_start, int raw_copy_size) {
132
  DCHECK(to_base->map() != isolate->heap()->fixed_cow_array_map());
133
  DisallowHeapAllocation no_allocation;
134 135
  int copy_size = raw_copy_size;
  if (raw_copy_size < 0) {
136
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
137
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
138 139
    copy_size = Min(from_base->length() - from_start,
                    to_base->length() - to_start);
140
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
141
      int start = to_start + copy_size;
142
      int length = to_base->length() - start;
143
      if (length > 0) {
144
        MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
145
                      isolate->heap()->the_hole_value(), length);
146 147
      }
    }
148
  }
149
  DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
150
         (copy_size + static_cast<int>(from_start)) <= from_base->length());
151
  if (copy_size == 0) return;
152 153
  FixedArray* from = FixedArray::cast(from_base);
  FixedArray* to = FixedArray::cast(to_base);
154 155
  DCHECK(IsSmiOrObjectElementsKind(from_kind));
  DCHECK(IsSmiOrObjectElementsKind(to_kind));
156 157

  WriteBarrierMode write_barrier_mode =
158
      (IsObjectElementsKind(from_kind) && IsObjectElementsKind(to_kind))
159 160 161 162 163
          ? UPDATE_WRITE_BARRIER
          : SKIP_WRITE_BARRIER;
  for (int i = 0; i < copy_size; i++) {
    Object* value = from->get(from_start + i);
    to->set(to_start + i, value, write_barrier_mode);
164 165 166
  }
}

167
static void CopyDictionaryToObjectElements(
168 169 170
    Isolate* isolate, FixedArrayBase* from_base, uint32_t from_start,
    FixedArrayBase* to_base, ElementsKind to_kind, uint32_t to_start,
    int raw_copy_size) {
171
  DisallowHeapAllocation no_allocation;
172
  NumberDictionary* from = NumberDictionary::cast(from_base);
173 174
  int copy_size = raw_copy_size;
  if (raw_copy_size < 0) {
175
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
176 177 178
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
    copy_size = from->max_number_key() + 1 - from_start;
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
179
      int start = to_start + copy_size;
180
      int length = to_base->length() - start;
181
      if (length > 0) {
182
        MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
183
                      isolate->heap()->the_hole_value(), length);
184 185 186
      }
    }
  }
187
  DCHECK(to_base != from_base);
188
  DCHECK(IsSmiOrObjectElementsKind(to_kind));
189
  if (copy_size == 0) return;
190
  FixedArray* to = FixedArray::cast(to_base);
191 192 193 194
  uint32_t to_length = to->length();
  if (to_start + copy_size > to_length) {
    copy_size = to_length - to_start;
  }
195
  WriteBarrierMode write_barrier_mode = GetWriteBarrierMode(to_kind);
196
  for (int i = 0; i < copy_size; i++) {
197
    int entry = from->FindEntry(isolate, i + from_start);
198
    if (entry != NumberDictionary::kNotFound) {
199
      Object* value = from->ValueAt(entry);
200
      DCHECK(!value->IsTheHole(isolate));
201
      to->set(i + to_start, value, write_barrier_mode);
202
    } else {
203
      to->set_the_hole(isolate, i + to_start);
204 205
    }
  }
206 207 208
}


209 210 211
// NOTE: this method violates the handlified function signature convention:
// raw pointer parameters in the function that allocates.
// See ElementsAccessorBase::CopyElements() for details.
212 213
static void CopyDoubleToObjectElements(Isolate* isolate,
                                       FixedArrayBase* from_base,
214
                                       uint32_t from_start,
215
                                       FixedArrayBase* to_base,
216
                                       uint32_t to_start, int raw_copy_size) {
217 218
  int copy_size = raw_copy_size;
  if (raw_copy_size < 0) {
219
    DisallowHeapAllocation no_allocation;
220
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
221
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
222 223
    copy_size = Min(from_base->length() - from_start,
                    to_base->length() - to_start);
224
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
225 226 227 228
      // Also initialize the area that will be copied over since HeapNumber
      // allocation below can cause an incremental marking step, requiring all
      // existing heap objects to be propertly initialized.
      int start = to_start;
229
      int length = to_base->length() - start;
230
      if (length > 0) {
231
        MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
232
                      isolate->heap()->the_hole_value(), length);
233 234
      }
    }
235
  }
236

237
  DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
238
         (copy_size + static_cast<int>(from_start)) <= from_base->length());
239
  if (copy_size == 0) return;
240 241 242 243 244

  // From here on, the code below could actually allocate. Therefore the raw
  // values are wrapped into handles.
  Handle<FixedDoubleArray> from(FixedDoubleArray::cast(from_base), isolate);
  Handle<FixedArray> to(FixedArray::cast(to_base), isolate);
245

246 247 248
  // Use an outer loop to not waste too much time on creating HandleScopes.
  // On the other hand we might overflow a single handle scope depending on
  // the copy_size.
249 250
  int offset = 0;
  while (offset < copy_size) {
251
    HandleScope scope(isolate);
252 253
    offset += 100;
    for (int i = offset - 100; i < offset && i < copy_size; ++i) {
254 255
      Handle<Object> value =
          FixedDoubleArray::get(*from, i + from_start, isolate);
256
      to->set(i + to_start, *value, UPDATE_WRITE_BARRIER);
257 258 259 260 261
    }
  }
}


262
static void CopyDoubleToDoubleElements(FixedArrayBase* from_base,
263
                                       uint32_t from_start,
264 265
                                       FixedArrayBase* to_base,
                                       uint32_t to_start, int raw_copy_size) {
266
  DisallowHeapAllocation no_allocation;
267 268
  int copy_size = raw_copy_size;
  if (raw_copy_size < 0) {
269
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
270
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
271 272
    copy_size = Min(from_base->length() - from_start,
                    to_base->length() - to_start);
273
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
274
      for (int i = to_start + copy_size; i < to_base->length(); ++i) {
275
        FixedDoubleArray::cast(to_base)->set_the_hole(i);
276 277
      }
    }
278
  }
279
  DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
280
         (copy_size + static_cast<int>(from_start)) <= from_base->length());
281
  if (copy_size == 0) return;
282 283
  FixedDoubleArray* from = FixedDoubleArray::cast(from_base);
  FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
284 285 286 287
  Address to_address = to->address() + FixedDoubleArray::kHeaderSize;
  Address from_address = from->address() + FixedDoubleArray::kHeaderSize;
  to_address += kDoubleSize * to_start;
  from_address += kDoubleSize * from_start;
288
  int words_per_double = (kDoubleSize / kPointerSize);
289 290
  CopyWords(reinterpret_cast<Object**>(to_address),
            reinterpret_cast<Object**>(from_address),
291
            static_cast<size_t>(words_per_double * copy_size));
292 293 294
}


295
static void CopySmiToDoubleElements(FixedArrayBase* from_base,
296
                                    uint32_t from_start,
297
                                    FixedArrayBase* to_base, uint32_t to_start,
298
                                    int raw_copy_size) {
299
  DisallowHeapAllocation no_allocation;
300 301
  int copy_size = raw_copy_size;
  if (raw_copy_size < 0) {
302
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
303
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
304
    copy_size = from_base->length() - from_start;
305
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
306
      for (int i = to_start + copy_size; i < to_base->length(); ++i) {
307
        FixedDoubleArray::cast(to_base)->set_the_hole(i);
308 309 310
      }
    }
  }
311
  DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
312
         (copy_size + static_cast<int>(from_start)) <= from_base->length());
313
  if (copy_size == 0) return;
314 315 316
  FixedArray* from = FixedArray::cast(from_base);
  FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
  Object* the_hole = from->GetHeap()->the_hole_value();
317 318 319
  for (uint32_t from_end = from_start + static_cast<uint32_t>(copy_size);
       from_start < from_end; from_start++, to_start++) {
    Object* hole_or_smi = from->get(from_start);
320
    if (hole_or_smi == the_hole) {
321 322
      to->set_the_hole(to_start);
    } else {
jgruber's avatar
jgruber committed
323
      to->set(to_start, Smi::ToInt(hole_or_smi));
324 325 326 327 328
    }
  }
}


329
static void CopyPackedSmiToDoubleElements(FixedArrayBase* from_base,
330
                                          uint32_t from_start,
331 332
                                          FixedArrayBase* to_base,
                                          uint32_t to_start, int packed_size,
333
                                          int raw_copy_size) {
334
  DisallowHeapAllocation no_allocation;
335 336 337
  int copy_size = raw_copy_size;
  uint32_t to_end;
  if (raw_copy_size < 0) {
338
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
339
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
340
    copy_size = packed_size - from_start;
341
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
342
      to_end = to_base->length();
343
      for (uint32_t i = to_start + copy_size; i < to_end; ++i) {
344
        FixedDoubleArray::cast(to_base)->set_the_hole(i);
345
      }
346 347 348 349 350 351
    } else {
      to_end = to_start + static_cast<uint32_t>(copy_size);
    }
  } else {
    to_end = to_start + static_cast<uint32_t>(copy_size);
  }
352 353 354
  DCHECK(static_cast<int>(to_end) <= to_base->length());
  DCHECK(packed_size >= 0 && packed_size <= copy_size);
  DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
355
         (copy_size + static_cast<int>(from_start)) <= from_base->length());
356
  if (copy_size == 0) return;
357 358
  FixedArray* from = FixedArray::cast(from_base);
  FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
359 360 361
  for (uint32_t from_end = from_start + static_cast<uint32_t>(packed_size);
       from_start < from_end; from_start++, to_start++) {
    Object* smi = from->get(from_start);
362
    DCHECK(!smi->IsTheHole());
jgruber's avatar
jgruber committed
363
    to->set(to_start, Smi::ToInt(smi));
364 365 366 367
  }
}


368
static void CopyObjectToDoubleElements(FixedArrayBase* from_base,
369
                                       uint32_t from_start,
370 371
                                       FixedArrayBase* to_base,
                                       uint32_t to_start, int raw_copy_size) {
372
  DisallowHeapAllocation no_allocation;
373 374
  int copy_size = raw_copy_size;
  if (raw_copy_size < 0) {
375
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
376
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
377
    copy_size = from_base->length() - from_start;
378
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
379
      for (int i = to_start + copy_size; i < to_base->length(); ++i) {
380
        FixedDoubleArray::cast(to_base)->set_the_hole(i);
381 382 383
      }
    }
  }
384
  DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
385
         (copy_size + static_cast<int>(from_start)) <= from_base->length());
386
  if (copy_size == 0) return;
387 388 389
  FixedArray* from = FixedArray::cast(from_base);
  FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
  Object* the_hole = from->GetHeap()->the_hole_value();
390 391 392
  for (uint32_t from_end = from_start + copy_size;
       from_start < from_end; from_start++, to_start++) {
    Object* hole_or_object = from->get(from_start);
393
    if (hole_or_object == the_hole) {
394
      to->set_the_hole(to_start);
395
    } else {
396
      to->set(to_start, hole_or_object->Number());
397 398 399 400
    }
  }
}

401 402 403
static void CopyDictionaryToDoubleElements(
    Isolate* isolate, FixedArrayBase* from_base, uint32_t from_start,
    FixedArrayBase* to_base, uint32_t to_start, int raw_copy_size) {
404
  DisallowHeapAllocation no_allocation;
405
  NumberDictionary* from = NumberDictionary::cast(from_base);
406 407
  int copy_size = raw_copy_size;
  if (copy_size < 0) {
408
    DCHECK(copy_size == ElementsAccessor::kCopyToEnd ||
409 410 411
           copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
    copy_size = from->max_number_key() + 1 - from_start;
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
412
      for (int i = to_start + copy_size; i < to_base->length(); ++i) {
413
        FixedDoubleArray::cast(to_base)->set_the_hole(i);
414 415 416 417
      }
    }
  }
  if (copy_size == 0) return;
418
  FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
419 420 421 422
  uint32_t to_length = to->length();
  if (to_start + copy_size > to_length) {
    copy_size = to_length - to_start;
  }
423
  for (int i = 0; i < copy_size; i++) {
424
    int entry = from->FindEntry(isolate, i + from_start);
425
    if (entry != NumberDictionary::kNotFound) {
426 427 428 429 430 431 432
      to->set(i + to_start, from->ValueAt(entry)->Number());
    } else {
      to->set_the_hole(i + to_start);
    }
  }
}

433 434
static void TraceTopFrame(Isolate* isolate) {
  StackFrameIterator it(isolate);
435 436 437 438 439 440
  if (it.done()) {
    PrintF("unknown location (no JavaScript frames present)");
    return;
  }
  StackFrame* raw_frame = it.frame();
  if (raw_frame->is_internal()) {
441 442 443 444
    Code* current_code_object =
        isolate->heap()->GcSafeFindCodeForInnerPointer(raw_frame->pc());
    if (current_code_object->builtin_index() ==
        Builtins::kFunctionPrototypeApply) {
445 446 447 448 449
      PrintF("apply from ");
      it.Advance();
      raw_frame = it.frame();
    }
  }
450
  JavaScriptFrame::PrintTop(isolate, stdout, false, true);
451 452
}

453
static void SortIndices(
454
    Isolate* isolate, Handle<FixedArray> indices, uint32_t sort_size,
455 456
    WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER) {
  struct {
457 458 459 460
    bool operator()(const base::AtomicElement<Object*>& elementA,
                    const base::AtomicElement<Object*>& elementB) {
      const Object* a = elementA.value();
      const Object* b = elementB.value();
461 462 463 464
      if (a->IsSmi() || !a->IsUndefined(HeapObject::cast(a)->GetIsolate())) {
        if (!b->IsSmi() && b->IsUndefined(HeapObject::cast(b)->GetIsolate())) {
          return true;
        }
465 466
        return a->Number() < b->Number();
      }
467
      return !b->IsSmi() && b->IsUndefined(HeapObject::cast(b)->GetIsolate());
468 469
    }
  } cmp;
470 471 472 473 474
  // Use AtomicElement wrapper to ensure that std::sort uses atomic load and
  // store operations that are safe for concurrent marking.
  base::AtomicElement<Object*>* start =
      reinterpret_cast<base::AtomicElement<Object*>*>(
          indices->GetFirstElementAddress());
475 476
  std::sort(start, start + sort_size, cmp);
  if (write_barrier_mode != SKIP_WRITE_BARRIER) {
477
    FIXED_ARRAY_ELEMENTS_WRITE_BARRIER(isolate->heap(), *indices, 0, sort_size);
478 479
  }
}
480

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
static Maybe<bool> IncludesValueSlowPath(Isolate* isolate,
                                         Handle<JSObject> receiver,
                                         Handle<Object> value,
                                         uint32_t start_from, uint32_t length) {
  bool search_for_hole = value->IsUndefined(isolate);
  for (uint32_t k = start_from; k < length; ++k) {
    LookupIterator it(isolate, receiver, k);
    if (!it.IsFound()) {
      if (search_for_hole) return Just(true);
      continue;
    }
    Handle<Object> element_k;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k,
                                     Object::GetProperty(&it), Nothing<bool>());

    if (value->SameValueZero(*element_k)) return Just(true);
  }

  return Just(false);
}

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
static Maybe<int64_t> IndexOfValueSlowPath(Isolate* isolate,
                                           Handle<JSObject> receiver,
                                           Handle<Object> value,
                                           uint32_t start_from,
                                           uint32_t length) {
  for (uint32_t k = start_from; k < length; ++k) {
    LookupIterator it(isolate, receiver, k);
    if (!it.IsFound()) {
      continue;
    }
    Handle<Object> element_k;
    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
        isolate, element_k, Object::GetProperty(&it), Nothing<int64_t>());

    if (value->StrictEquals(*element_k)) return Just<int64_t>(k);
  }

  return Just<int64_t>(-1);
}

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
// The InternalElementsAccessor is a helper class to expose otherwise protected
// methods to its subclasses. Namely, we don't want to publicly expose methods
// that take an entry (instead of an index) as an argument.
class InternalElementsAccessor : public ElementsAccessor {
 public:
  explicit InternalElementsAccessor(const char* name)
      : ElementsAccessor(name) {}

  virtual uint32_t GetEntryForIndex(Isolate* isolate, JSObject* holder,
                                    FixedArrayBase* backing_store,
                                    uint32_t index) = 0;

  virtual PropertyDetails GetDetails(JSObject* holder, uint32_t entry) = 0;
};

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
// Base class for element handler implementations. Contains the
// the common logic for objects with different ElementsKinds.
// Subclasses must specialize method for which the element
// implementation differs from the base class implementation.
//
// This class is intended to be used in the following way:
//
//   class SomeElementsAccessor :
//       public ElementsAccessorBase<SomeElementsAccessor,
//                                   BackingStoreClass> {
//     ...
//   }
//
// This is an example of the Curiously Recurring Template Pattern (see
// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).  We use
// CRTP to guarantee aggressive compile time optimizations (i.e.  inlining and
// specialization of SomeElementsAccessor methods).
554
template <typename Subclass, typename ElementsTraitsParam>
555
class ElementsAccessorBase : public InternalElementsAccessor {
556
 public:
557
  explicit ElementsAccessorBase(const char* name)
558
      : InternalElementsAccessor(name) {}
559 560 561 562

  typedef ElementsTraitsParam ElementsTraits;
  typedef typename ElementsTraitsParam::BackingStore BackingStore;

563
  static ElementsKind kind() { return ElementsTraits::Kind; }
564

565
  static void ValidateContents(JSObject* holder, int length) {}
566

567 568
  static void ValidateImpl(JSObject* holder) {
    FixedArrayBase* fixed_array_base = holder->elements();
569 570
    if (!fixed_array_base->IsHeapObject()) return;
    // Arrays that have been shifted in place can't be verified.
571
    if (fixed_array_base->IsFiller()) return;
572 573
    int length = 0;
    if (holder->IsJSArray()) {
574
      Object* length_obj = JSArray::cast(holder)->length();
575
      if (length_obj->IsSmi()) {
jgruber's avatar
jgruber committed
576
        length = Smi::ToInt(length_obj);
577 578 579 580
      }
    } else {
      length = fixed_array_base->length();
    }
581
    Subclass::ValidateContents(holder, length);
582 583
  }

584
  void Validate(JSObject* holder) final {
585
    DisallowHeapAllocation no_gc;
586
    Subclass::ValidateImpl(holder);
587 588
  }

589 590 591
  static bool IsPackedImpl(JSObject* holder, FixedArrayBase* backing_store,
                           uint32_t start, uint32_t end) {
    DisallowHeapAllocation no_gc;
592
    if (IsFastPackedElementsKind(kind())) return true;
593
    Isolate* isolate = holder->GetIsolate();
594
    for (uint32_t i = start; i < end; i++) {
595 596
      if (!Subclass::HasElementImpl(isolate, holder, i, backing_store,
                                    ALL_PROPERTIES)) {
597 598 599 600 601 602
        return false;
      }
    }
    return true;
  }

603
  static void TryTransitionResultArrayToPacked(Handle<JSArray> array) {
604
    if (!IsHoleyOrDictionaryElementsKind(kind())) return;
605 606
    Handle<FixedArrayBase> backing_store(array->elements(),
                                         array->GetIsolate());
jgruber's avatar
jgruber committed
607
    int length = Smi::ToInt(array->length());
608
    if (!Subclass::IsPackedImpl(*array, *backing_store, 0, length)) {
609 610 611 612 613 614 615 616 617 618 619 620
      return;
    }
    ElementsKind packed_kind = GetPackedElementsKind(kind());
    Handle<Map> new_map =
        JSObject::GetElementsTransitionMap(array, packed_kind);
    JSObject::MigrateToMap(array, new_map);
    if (FLAG_trace_elements_transitions) {
      JSObject::PrintElementsTransition(stdout, array, kind(), backing_store,
                                        packed_kind, backing_store);
    }
  }

621 622
  bool HasElement(JSObject* holder, uint32_t index,
                  FixedArrayBase* backing_store, PropertyFilter filter) final {
623 624
    return Subclass::HasElementImpl(holder->GetIsolate(), holder, index,
                                    backing_store, filter);
625 626
  }

627 628
  static bool HasElementImpl(Isolate* isolate, JSObject* holder, uint32_t index,
                             FixedArrayBase* backing_store,
629
                             PropertyFilter filter = ALL_PROPERTIES) {
630 631
    return Subclass::GetEntryForIndexImpl(isolate, holder, backing_store, index,
                                          filter) != kMaxUInt32;
632 633
  }

634 635 636 637 638 639 640 641 642 643
  bool HasEntry(JSObject* holder, uint32_t entry) final {
    return Subclass::HasEntryImpl(holder->GetIsolate(), holder->elements(),
                                  entry);
  }

  static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* backing_store,
                           uint32_t entry) {
    UNIMPLEMENTED();
  }

644
  bool HasAccessors(JSObject* holder) final {
645
    return Subclass::HasAccessorsImpl(holder, holder->elements());
646 647 648 649 650 651 652
  }

  static bool HasAccessorsImpl(JSObject* holder,
                               FixedArrayBase* backing_store) {
    return false;
  }

653
  Handle<Object> Get(Handle<JSObject> holder, uint32_t entry) final {
654
    return Subclass::GetInternalImpl(holder, entry);
655 656
  }

657 658 659
  static Handle<Object> GetInternalImpl(Handle<JSObject> holder,
                                        uint32_t entry) {
    return Subclass::GetImpl(holder->GetIsolate(), holder->elements(), entry);
660 661
  }

662 663
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* backing_store,
                                uint32_t entry) {
664 665
    uint32_t index = GetIndexForEntryImpl(backing_store, entry);
    return handle(BackingStore::cast(backing_store)->get(index), isolate);
666 667
  }

668
  void Set(Handle<JSObject> holder, uint32_t entry, Object* value) final {
669
    Subclass::SetImpl(holder, entry, value);
670 671
  }

672 673 674
  void Reconfigure(Handle<JSObject> object, Handle<FixedArrayBase> store,
                   uint32_t entry, Handle<Object> value,
                   PropertyAttributes attributes) final {
675
    Subclass::ReconfigureImpl(object, store, entry, value, attributes);
676 677 678
  }

  static void ReconfigureImpl(Handle<JSObject> object,
679
                              Handle<FixedArrayBase> store, uint32_t entry,
680 681 682 683 684
                              Handle<Object> value,
                              PropertyAttributes attributes) {
    UNREACHABLE();
  }

685 686
  void Add(Handle<JSObject> object, uint32_t index, Handle<Object> value,
           PropertyAttributes attributes, uint32_t new_capacity) final {
687
    Subclass::AddImpl(object, index, value, attributes, new_capacity);
688 689
  }

690
  static void AddImpl(Handle<JSObject> object, uint32_t index,
691 692 693 694 695
                      Handle<Object> value, PropertyAttributes attributes,
                      uint32_t new_capacity) {
    UNREACHABLE();
  }

696 697
  uint32_t Push(Handle<JSArray> receiver, Arguments* args,
                uint32_t push_size) final {
698
    return Subclass::PushImpl(receiver, args, push_size);
699 700
  }

701
  static uint32_t PushImpl(Handle<JSArray> receiver, Arguments* args,
702
                           uint32_t push_sized) {
703 704 705
    UNREACHABLE();
  }

706
  uint32_t Unshift(Handle<JSArray> receiver, Arguments* args,
707
                   uint32_t unshift_size) final {
708
    return Subclass::UnshiftImpl(receiver, args, unshift_size);
709 710
  }

711
  static uint32_t UnshiftImpl(Handle<JSArray> receiver, Arguments* args,
712 713 714 715
                              uint32_t unshift_size) {
    UNREACHABLE();
  }

716 717
  Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
                         uint32_t end) final {
718
    return Subclass::SliceImpl(receiver, start, end);
719 720
  }

721 722
  static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
                                    uint32_t end) {
723
    UNREACHABLE();
724 725
  }

726
  Handle<JSArray> Splice(Handle<JSArray> receiver, uint32_t start,
727 728
                         uint32_t delete_count, Arguments* args,
                         uint32_t add_count) final {
729
    return Subclass::SpliceImpl(receiver, start, delete_count, args, add_count);
730 731 732 733
  }

  static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver,
                                    uint32_t start, uint32_t delete_count,
734
                                    Arguments* args, uint32_t add_count) {
735 736 737
    UNREACHABLE();
  }

738
  Handle<Object> Pop(Handle<JSArray> receiver) final {
739
    return Subclass::PopImpl(receiver);
cbruni's avatar
cbruni committed
740 741
  }

742
  static Handle<Object> PopImpl(Handle<JSArray> receiver) {
cbruni's avatar
cbruni committed
743 744
    UNREACHABLE();
  }
745

746
  Handle<Object> Shift(Handle<JSArray> receiver) final {
747
    return Subclass::ShiftImpl(receiver);
748 749
  }

750
  static Handle<Object> ShiftImpl(Handle<JSArray> receiver) {
751 752 753
    UNREACHABLE();
  }

754
  void SetLength(Handle<JSArray> array, uint32_t length) final {
755
    Subclass::SetLengthImpl(array->GetIsolate(), array, length,
756
                            handle(array->elements(), array->GetIsolate()));
757 758
  }

759 760
  static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
                            uint32_t length,
761 762 763 764 765 766 767 768
                            Handle<FixedArrayBase> backing_store) {
    DCHECK(!array->SetLengthWouldNormalize(length));
    DCHECK(IsFastElementsKind(array->GetElementsKind()));
    uint32_t old_length = 0;
    CHECK(array->length()->ToArrayIndex(&old_length));

    if (old_length < length) {
      ElementsKind kind = array->GetElementsKind();
769
      if (!IsHoleyElementsKind(kind)) {
770 771 772 773 774 775 776
        kind = GetHoleyElementsKind(kind);
        JSObject::TransitionElementsKind(array, kind);
      }
    }

    // Check whether the backing store should be shrunk.
    uint32_t capacity = backing_store->length();
777
    old_length = Min(old_length, capacity);
778 779 780
    if (length == 0) {
      array->initialize_elements();
    } else if (length <= capacity) {
781
      if (IsSmiOrObjectElementsKind(kind())) {
782 783 784 785
        JSObject::EnsureWritableFastElements(array);
        if (array->elements() != *backing_store) {
          backing_store = handle(array->elements(), isolate);
        }
786
      }
787
      if (2 * length + JSObject::kMinAddedElementsCapacity <= capacity) {
788
        // If more than half the elements won't be used, trim the array.
789 790 791 792 793 794 795
        // Do not trim from short arrays to prevent frequent trimming on
        // repeated pop operations.
        // Leave some space to allow for subsequent push operations.
        int elements_to_trim = length + 1 == old_length
                                   ? (capacity - length) / 2
                                   : capacity - length;
        isolate->heap()->RightTrimFixedArray(*backing_store, elements_to_trim);
796 797 798 799
        // Fill the non-trimmed elements with holes.
        BackingStore::cast(*backing_store)
            ->FillWithHoles(length,
                            std::min(old_length, capacity - elements_to_trim));
800 801
      } else {
        // Otherwise, fill the unused tail with holes.
802
        BackingStore::cast(*backing_store)->FillWithHoles(length, old_length);
803 804 805 806
      }
    } else {
      // Check whether the backing store should be expanded.
      capacity = Max(length, JSObject::NewElementsCapacity(capacity));
807
      Subclass::GrowCapacityAndConvertImpl(array, capacity);
808 809 810
    }

    array->set_length(Smi::FromInt(length));
811
    JSObject::ValidateElements(*array);
812
  }
813

814 815 816 817 818 819 820 821 822
  uint32_t NumberOfElements(JSObject* receiver) final {
    return Subclass::NumberOfElementsImpl(receiver, receiver->elements());
  }

  static uint32_t NumberOfElementsImpl(JSObject* receiver,
                                       FixedArrayBase* backing_store) {
    UNREACHABLE();
  }

823
  static uint32_t GetMaxIndex(JSObject* receiver, FixedArrayBase* elements) {
824
    if (receiver->IsJSArray()) {
825
      DCHECK(JSArray::cast(receiver)->length()->IsSmi());
826
      return static_cast<uint32_t>(
jgruber's avatar
jgruber committed
827
          Smi::ToInt(JSArray::cast(receiver)->length()));
828
    }
829
    return Subclass::GetCapacityImpl(receiver, elements);
830 831
  }

832 833 834 835 836
  static uint32_t GetMaxNumberOfEntries(JSObject* receiver,
                                        FixedArrayBase* elements) {
    return Subclass::GetMaxIndex(receiver, elements);
  }

837 838 839
  static Handle<FixedArrayBase> ConvertElementsWithCapacity(
      Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
      ElementsKind from_kind, uint32_t capacity) {
840
    return ConvertElementsWithCapacity(
841
        object, old_elements, from_kind, capacity, 0, 0,
842 843 844 845 846 847
        ElementsAccessor::kCopyToEndAndInitializeToHole);
  }

  static Handle<FixedArrayBase> ConvertElementsWithCapacity(
      Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
      ElementsKind from_kind, uint32_t capacity, int copy_size) {
848 849 850 851 852 853 854 855
    return ConvertElementsWithCapacity(object, old_elements, from_kind,
                                       capacity, 0, 0, copy_size);
  }

  static Handle<FixedArrayBase> ConvertElementsWithCapacity(
      Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
      ElementsKind from_kind, uint32_t capacity, uint32_t src_index,
      uint32_t dst_index, int copy_size) {
856
    Isolate* isolate = object->GetIsolate();
857
    Handle<FixedArrayBase> new_elements;
858
    if (IsDoubleElementsKind(kind())) {
859
      new_elements = isolate->factory()->NewFixedDoubleArray(capacity);
860
    } else {
861
      new_elements = isolate->factory()->NewUninitializedFixedArray(capacity);
862 863
    }

864
    int packed_size = kPackedSizeNotKnown;
865
    if (IsFastPackedElementsKind(from_kind) && object->IsJSArray()) {
jgruber's avatar
jgruber committed
866
      packed_size = Smi::ToInt(JSArray::cast(*object)->length());
867 868
    }

869
    Subclass::CopyElementsImpl(isolate, *old_elements, src_index, *new_elements,
870
                               from_kind, dst_index, packed_size, copy_size);
871 872

    return new_elements;
873 874
  }

875 876
  static void TransitionElementsKindImpl(Handle<JSObject> object,
                                         Handle<Map> to_map) {
877
    Handle<Map> from_map = handle(object->map(), object->GetIsolate());
878 879
    ElementsKind from_kind = from_map->elements_kind();
    ElementsKind to_kind = to_map->elements_kind();
880
    if (IsHoleyElementsKind(from_kind)) {
881 882 883 884 885 886 887 888
      to_kind = GetHoleyElementsKind(to_kind);
    }
    if (from_kind != to_kind) {
      // This method should never be called for any other case.
      DCHECK(IsFastElementsKind(from_kind));
      DCHECK(IsFastElementsKind(to_kind));
      DCHECK_NE(TERMINAL_FAST_ELEMENTS_KIND, from_kind);

889 890
      Handle<FixedArrayBase> from_elements(object->elements(),
                                           object->GetIsolate());
891
      if (object->elements() == object->GetHeap()->empty_fixed_array() ||
892
          IsDoubleElementsKind(from_kind) == IsDoubleElementsKind(to_kind)) {
893 894 895 896
        // No change is needed to the elements() buffer, the transition
        // only requires a map change.
        JSObject::MigrateToMap(object, to_map);
      } else {
897 898 899
        DCHECK(
            (IsSmiElementsKind(from_kind) && IsDoubleElementsKind(to_kind)) ||
            (IsDoubleElementsKind(from_kind) && IsObjectElementsKind(to_kind)));
900 901 902 903 904 905
        uint32_t capacity = static_cast<uint32_t>(object->elements()->length());
        Handle<FixedArrayBase> elements = ConvertElementsWithCapacity(
            object, from_elements, from_kind, capacity);
        JSObject::SetMapAndElements(object, to_map, elements);
      }
      if (FLAG_trace_elements_transitions) {
906 907 908
        JSObject::PrintElementsTransition(
            stdout, object, from_kind, from_elements, to_kind,
            handle(object->elements(), object->GetIsolate()));
909 910 911 912
      }
    }
  }

913
  static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
914
                                         uint32_t capacity) {
915
    ElementsKind from_kind = object->GetElementsKind();
916
    if (IsSmiOrObjectElementsKind(from_kind)) {
917 918 919
      // Array optimizations rely on the prototype lookups of Array objects
      // always returning undefined. If there is a store to the initial
      // prototype object, make sure all of these optimizations are invalidated.
920
      object->GetIsolate()->UpdateNoElementsProtectorOnSetLength(object);
921
    }
922 923
    Handle<FixedArrayBase> old_elements(object->elements(),
                                        object->GetIsolate());
924 925
    // This method should only be called if there's a reason to update the
    // elements.
926
    DCHECK(IsDoubleElementsKind(from_kind) != IsDoubleElementsKind(kind()) ||
927 928
           IsDictionaryElementsKind(from_kind) ||
           static_cast<uint32_t>(old_elements->length()) < capacity);
929 930 931 932 933 934 935
    Subclass::BasicGrowCapacityAndConvertImpl(object, old_elements, from_kind,
                                              kind(), capacity);
  }

  static void BasicGrowCapacityAndConvertImpl(
      Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
      ElementsKind from_kind, ElementsKind to_kind, uint32_t capacity) {
936 937 938
    Handle<FixedArrayBase> elements =
        ConvertElementsWithCapacity(object, old_elements, from_kind, capacity);

939 940
    if (IsHoleyOrDictionaryElementsKind(from_kind))
      to_kind = GetHoleyElementsKind(to_kind);
941 942 943 944 945 946 947 948 949 950
    Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, to_kind);
    JSObject::SetMapAndElements(object, new_map, elements);

    // Transition through the allocation site as well if present.
    JSObject::UpdateAllocationSite(object, to_kind);

    if (FLAG_trace_elements_transitions) {
      JSObject::PrintElementsTransition(stdout, object, from_kind, old_elements,
                                        to_kind, elements);
    }
951 952
  }

953 954 955 956
  void TransitionElementsKind(Handle<JSObject> object, Handle<Map> map) final {
    Subclass::TransitionElementsKindImpl(object, map);
  }

957 958
  void GrowCapacityAndConvert(Handle<JSObject> object,
                              uint32_t capacity) final {
959
    Subclass::GrowCapacityAndConvertImpl(object, capacity);
960 961
  }

962 963 964 965 966 967 968
  bool GrowCapacity(Handle<JSObject> object, uint32_t index) final {
    // This function is intended to be called from optimized code. We don't
    // want to trigger lazy deopts there, so refuse to handle cases that would.
    if (object->map()->is_prototype_map() ||
        object->WouldConvertToSlowElements(index)) {
      return false;
    }
969 970
    Handle<FixedArrayBase> old_elements(object->elements(),
                                        object->GetIsolate());
971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
    uint32_t new_capacity = JSObject::NewElementsCapacity(index + 1);
    DCHECK(static_cast<uint32_t>(old_elements->length()) < new_capacity);
    Handle<FixedArrayBase> elements =
        ConvertElementsWithCapacity(object, old_elements, kind(), new_capacity);

    DCHECK_EQ(object->GetElementsKind(), kind());
    // Transition through the allocation site as well if present.
    if (JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kCheckOnly>(
            object, kind())) {
      return false;
    }

    object->set_elements(*elements);
    return true;
  }

987
  void Delete(Handle<JSObject> obj, uint32_t entry) final {
988
    Subclass::DeleteImpl(obj, entry);
989
  }
990

991 992 993 994
  static void CopyElementsImpl(Isolate* isolate, FixedArrayBase* from,
                               uint32_t from_start, FixedArrayBase* to,
                               ElementsKind from_kind, uint32_t to_start,
                               int packed_size, int copy_size) {
995
    UNREACHABLE();
996 997
  }

998 999 1000
  void CopyElements(JSObject* from_holder, uint32_t from_start,
                    ElementsKind from_kind, Handle<FixedArrayBase> to,
                    uint32_t to_start, int copy_size) final {
1001 1002 1003 1004
    int packed_size = kPackedSizeNotKnown;
    bool is_packed = IsFastPackedElementsKind(from_kind) &&
        from_holder->IsJSArray();
    if (is_packed) {
jgruber's avatar
jgruber committed
1005
      packed_size = Smi::ToInt(JSArray::cast(from_holder)->length());
1006 1007
      if (copy_size >= 0 && packed_size > copy_size) {
        packed_size = copy_size;
1008 1009
      }
    }
1010
    FixedArrayBase* from = from_holder->elements();
1011
    // NOTE: the Subclass::CopyElementsImpl() methods
1012 1013 1014 1015 1016 1017 1018 1019
    // violate the handlified function signature convention:
    // raw pointer parameters in the function that allocates. This is done
    // intentionally to avoid ArrayConcat() builtin performance degradation.
    //
    // Details: The idea is that allocations actually happen only in case of
    // copying from object with fast double elements to object with object
    // elements. In all the other cases there are no allocations performed and
    // handle creation causes noticeable performance degradation of the builtin.
1020 1021
    Subclass::CopyElementsImpl(from_holder->GetIsolate(), from, from_start, *to,
                               from_kind, to_start, packed_size, copy_size);
1022 1023
  }

1024 1025
  void CopyElements(Isolate* isolate, Handle<FixedArrayBase> source,
                    ElementsKind source_kind,
1026
                    Handle<FixedArrayBase> destination, int size) {
1027 1028
    Subclass::CopyElementsImpl(isolate, *source, 0, *destination, source_kind,
                               0, kPackedSizeNotKnown, size);
1029 1030
  }

1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
  void CopyTypedArrayElementsSlice(JSTypedArray* source,
                                   JSTypedArray* destination, size_t start,
                                   size_t end) {
    Subclass::CopyTypedArrayElementsSliceImpl(source, destination, start, end);
  }

  static void CopyTypedArrayElementsSliceImpl(JSTypedArray* source,
                                              JSTypedArray* destination,
                                              size_t start, size_t end) {
    UNREACHABLE();
  }

1043
  Object* CopyElements(Handle<Object> source, Handle<JSObject> destination,
1044 1045 1046
                       size_t length, uint32_t offset) final {
    return Subclass::CopyElementsHandleImpl(source, destination, length,
                                            offset);
1047 1048
  }

1049
  static Object* CopyElementsHandleImpl(Handle<Object> source,
1050
                                        Handle<JSObject> destination,
1051
                                        size_t length, uint32_t offset) {
1052 1053 1054
    UNREACHABLE();
  }

1055
  Handle<NumberDictionary> Normalize(Handle<JSObject> object) final {
1056 1057
    return Subclass::NormalizeImpl(
        object, handle(object->elements(), object->GetIsolate()));
1058 1059
  }

1060
  static Handle<NumberDictionary> NormalizeImpl(
1061 1062 1063 1064
      Handle<JSObject> object, Handle<FixedArrayBase> elements) {
    UNREACHABLE();
  }

1065 1066 1067 1068
  Maybe<bool> CollectValuesOrEntries(Isolate* isolate, Handle<JSObject> object,
                                     Handle<FixedArray> values_or_entries,
                                     bool get_entries, int* nof_items,
                                     PropertyFilter filter) {
1069
    return Subclass::CollectValuesOrEntriesImpl(
1070 1071 1072 1073 1074 1075 1076
        isolate, object, values_or_entries, get_entries, nof_items, filter);
  }

  static Maybe<bool> CollectValuesOrEntriesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
      PropertyFilter filter) {
1077
    DCHECK_EQ(*nof_items, 0);
1078 1079
    KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
                               ALL_PROPERTIES);
1080
    Subclass::CollectElementIndicesImpl(
1081
        object, handle(object->elements(), isolate), &accumulator);
1082 1083
    Handle<FixedArray> keys = accumulator.GetKeys();

1084 1085
    int count = 0;
    int i = 0;
1086
    ElementsKind original_elements_kind = object->GetElementsKind();
1087 1088

    for (; i < keys->length(); ++i) {
1089 1090 1091 1092
      Handle<Object> key(keys->get(i), isolate);
      uint32_t index;
      if (!key->ToUint32(&index)) continue;

1093
      DCHECK_EQ(object->GetElementsKind(), original_elements_kind);
1094
      uint32_t entry = Subclass::GetEntryForIndexImpl(
1095
          isolate, *object, object->elements(), index, filter);
1096
      if (entry == kMaxUInt32) continue;
1097
      PropertyDetails details = Subclass::GetDetailsImpl(*object, entry);
1098

1099
      Handle<Object> value;
1100
      if (details.kind() == kData) {
1101
        value = Subclass::GetImpl(isolate, object->elements(), entry);
1102
      } else {
1103
        // This might modify the elements and/or change the elements kind.
1104 1105 1106 1107
        LookupIterator it(isolate, object, index, LookupIterator::OWN);
        ASSIGN_RETURN_ON_EXCEPTION_VALUE(
            isolate, value, Object::GetProperty(&it), Nothing<bool>());
      }
1108 1109
      if (get_entries) value = MakeEntryPair(isolate, index, value);
      values_or_entries->set(count++, *value);
1110
      if (object->GetElementsKind() != original_elements_kind) break;
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
    }

    // Slow path caused by changes in elements kind during iteration.
    for (; i < keys->length(); i++) {
      Handle<Object> key(keys->get(i), isolate);
      uint32_t index;
      if (!key->ToUint32(&index)) continue;

      if (filter & ONLY_ENUMERABLE) {
        InternalElementsAccessor* accessor =
            reinterpret_cast<InternalElementsAccessor*>(
                object->GetElementsAccessor());
        uint32_t entry = accessor->GetEntryForIndex(isolate, *object,
                                                    object->elements(), index);
        if (entry == kMaxUInt32) continue;
        PropertyDetails details = accessor->GetDetails(*object, entry);
        if (!details.IsEnumerable()) continue;
1128
      }
1129 1130 1131 1132 1133 1134 1135

      Handle<Object> value;
      LookupIterator it(isolate, object, index, LookupIterator::OWN);
      ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::GetProperty(&it),
                                       Nothing<bool>());

      if (get_entries) value = MakeEntryPair(isolate, index, value);
1136 1137 1138 1139 1140 1141 1142
      values_or_entries->set(count++, *value);
    }

    *nof_items = count;
    return Just(true);
  }

1143 1144
  void CollectElementIndices(Handle<JSObject> object,
                             Handle<FixedArrayBase> backing_store,
1145 1146 1147
                             KeyAccumulator* keys) final {
    if (keys->filter() & ONLY_ALL_CAN_READ) return;
    Subclass::CollectElementIndicesImpl(object, backing_store, keys);
1148 1149
  }

1150 1151
  static void CollectElementIndicesImpl(Handle<JSObject> object,
                                        Handle<FixedArrayBase> backing_store,
1152
                                        KeyAccumulator* keys) {
1153
    DCHECK_NE(DICTIONARY_ELEMENTS, kind());
1154
    // Non-dictionary elements can't have all-can-read accessors.
1155
    uint32_t length = Subclass::GetMaxIndex(*object, *backing_store);
1156
    PropertyFilter filter = keys->filter();
1157 1158
    Isolate* isolate = keys->isolate();
    Factory* factory = isolate->factory();
1159
    for (uint32_t i = 0; i < length; i++) {
1160 1161
      if (Subclass::HasElementImpl(isolate, *object, i, *backing_store,
                                   filter)) {
1162
        keys->AddKey(factory->NewNumberFromUint(i));
1163 1164 1165 1166
      }
    }
  }

1167 1168 1169
  static Handle<FixedArray> DirectCollectElementIndicesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
1170 1171
      PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices,
      uint32_t insertion_index = 0) {
1172
    uint32_t length = Subclass::GetMaxIndex(*object, *backing_store);
1173
    for (uint32_t i = 0; i < length; i++) {
1174 1175
      if (Subclass::HasElementImpl(isolate, *object, i, *backing_store,
                                   filter)) {
1176
        if (convert == GetKeysConversion::kConvertToString) {
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
          Handle<String> index_string = isolate->factory()->Uint32ToString(i);
          list->set(insertion_index, *index_string);
        } else {
          list->set(insertion_index, Smi::FromInt(i), SKIP_WRITE_BARRIER);
        }
        insertion_index++;
      }
    }
    *nof_indices = insertion_index;
    return list;
  }

1189 1190 1191 1192
  MaybeHandle<FixedArray> PrependElementIndices(
      Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
      Handle<FixedArray> keys, GetKeysConversion convert,
      PropertyFilter filter) final {
1193 1194
    return Subclass::PrependElementIndicesImpl(object, backing_store, keys,
                                               convert, filter);
1195 1196
  }

1197
  static MaybeHandle<FixedArray> PrependElementIndicesImpl(
1198 1199 1200 1201 1202 1203
      Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
      Handle<FixedArray> keys, GetKeysConversion convert,
      PropertyFilter filter) {
    Isolate* isolate = object->GetIsolate();
    uint32_t nof_property_keys = keys->length();
    uint32_t initial_list_length =
1204
        Subclass::GetMaxNumberOfEntries(*object, *backing_store);
1205

1206
    initial_list_length += nof_property_keys;
1207 1208 1209 1210 1211
    if (initial_list_length > FixedArray::kMaxLength ||
        initial_list_length < nof_property_keys) {
      return isolate->Throw<FixedArray>(isolate->factory()->NewRangeError(
          MessageTemplate::kInvalidArrayLength));
    }
1212 1213

    // Collect the element indices into a new list.
1214 1215 1216 1217 1218 1219 1220 1221
    MaybeHandle<FixedArray> raw_array =
        isolate->factory()->TryNewFixedArray(initial_list_length);
    Handle<FixedArray> combined_keys;

    // If we have a holey backing store try to precisely estimate the backing
    // store size as a last emergency measure if we cannot allocate the big
    // array.
    if (!raw_array.ToHandle(&combined_keys)) {
1222
      if (IsHoleyOrDictionaryElementsKind(kind())) {
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
        // If we overestimate the result list size we might end up in the
        // large-object space which doesn't free memory on shrinking the list.
        // Hence we try to estimate the final size for holey backing stores more
        // precisely here.
        initial_list_length =
            Subclass::NumberOfElementsImpl(*object, *backing_store);
        initial_list_length += nof_property_keys;
      }
      combined_keys = isolate->factory()->NewFixedArray(initial_list_length);
    }

1234
    uint32_t nof_indices = 0;
1235 1236
    bool needs_sorting = IsDictionaryElementsKind(kind()) ||
                         IsSloppyArgumentsElementsKind(kind());
1237
    combined_keys = Subclass::DirectCollectElementIndicesImpl(
1238 1239 1240
        isolate, object, backing_store,
        needs_sorting ? GetKeysConversion::kKeepNumbers : convert, filter,
        combined_keys, &nof_indices);
1241

1242
    if (needs_sorting) {
1243
      SortIndices(isolate, combined_keys, nof_indices);
1244 1245
      // Indices from dictionary elements should only be converted after
      // sorting.
1246
      if (convert == GetKeysConversion::kConvertToString) {
1247 1248
        for (uint32_t i = 0; i < nof_indices; i++) {
          Handle<Object> index_string = isolate->factory()->Uint32ToString(
1249
              combined_keys->get(i)->Number());
1250 1251 1252 1253 1254 1255
          combined_keys->set(i, *index_string);
        }
      }
    }

    // Copy over the passed-in property keys.
1256 1257 1258
    CopyObjectToObjectElements(isolate, *keys, PACKED_ELEMENTS, 0,
                               *combined_keys, PACKED_ELEMENTS, nof_indices,
                               nof_property_keys);
1259

1260 1261
    // For holey elements and arguments we might have to shrink the collected
    // keys since the estimates might be off.
1262 1263
    if (IsHoleyOrDictionaryElementsKind(kind()) ||
        IsSloppyArgumentsElementsKind(kind())) {
1264 1265 1266
      // Shrink combined_keys to the final size.
      int final_size = nof_indices + nof_property_keys;
      DCHECK_LE(final_size, combined_keys->length());
1267
      return FixedArray::ShrinkOrEmpty(combined_keys, final_size);
1268 1269 1270 1271
    }

    return combined_keys;
  }
1272

1273 1274 1275
  void AddElementsToKeyAccumulator(Handle<JSObject> receiver,
                                   KeyAccumulator* accumulator,
                                   AddKeyConversion convert) final {
1276
    Subclass::AddElementsToKeyAccumulatorImpl(receiver, accumulator, convert);
1277 1278
  }

1279 1280
  static uint32_t GetCapacityImpl(JSObject* holder,
                                  FixedArrayBase* backing_store) {
1281
    return backing_store->length();
1282 1283
  }

1284
  uint32_t GetCapacity(JSObject* holder, FixedArrayBase* backing_store) final {
1285
    return Subclass::GetCapacityImpl(holder, backing_store);
1286 1287
  }

1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
  static Object* FillImpl(Isolate* isolate, Handle<JSObject> receiver,
                          Handle<Object> obj_value, uint32_t start,
                          uint32_t end) {
    UNREACHABLE();
  }

  Object* Fill(Isolate* isolate, Handle<JSObject> receiver,
               Handle<Object> obj_value, uint32_t start, uint32_t end) {
    return Subclass::FillImpl(isolate, receiver, obj_value, start, end);
  }

1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312
  static Maybe<bool> IncludesValueImpl(Isolate* isolate,
                                       Handle<JSObject> receiver,
                                       Handle<Object> value,
                                       uint32_t start_from, uint32_t length) {
    return IncludesValueSlowPath(isolate, receiver, value, start_from, length);
  }

  Maybe<bool> IncludesValue(Isolate* isolate, Handle<JSObject> receiver,
                            Handle<Object> value, uint32_t start_from,
                            uint32_t length) final {
    return Subclass::IncludesValueImpl(isolate, receiver, value, start_from,
                                       length);
  }

1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326
  static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
                                         Handle<JSObject> receiver,
                                         Handle<Object> value,
                                         uint32_t start_from, uint32_t length) {
    return IndexOfValueSlowPath(isolate, receiver, value, start_from, length);
  }

  Maybe<int64_t> IndexOfValue(Isolate* isolate, Handle<JSObject> receiver,
                              Handle<Object> value, uint32_t start_from,
                              uint32_t length) final {
    return Subclass::IndexOfValueImpl(isolate, receiver, value, start_from,
                                      length);
  }

1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339
  static Maybe<int64_t> LastIndexOfValueImpl(Isolate* isolate,
                                             Handle<JSObject> receiver,
                                             Handle<Object> value,
                                             uint32_t start_from) {
    UNREACHABLE();
  }

  Maybe<int64_t> LastIndexOfValue(Isolate* isolate, Handle<JSObject> receiver,
                                  Handle<Object> value,
                                  uint32_t start_from) final {
    return Subclass::LastIndexOfValueImpl(isolate, receiver, value, start_from);
  }

1340 1341 1342 1343
  static void ReverseImpl(JSObject* receiver) { UNREACHABLE(); }

  void Reverse(JSObject* receiver) final { Subclass::ReverseImpl(receiver); }

1344 1345 1346
  static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store,
                                       uint32_t entry) {
    return entry;
1347 1348
  }

1349
  static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder,
1350
                                       FixedArrayBase* backing_store,
1351
                                       uint32_t index, PropertyFilter filter) {
1352
    uint32_t length = Subclass::GetMaxIndex(holder, backing_store);
1353
    if (IsHoleyOrDictionaryElementsKind(kind())) {
1354
      return index < length &&
1355 1356
                     !BackingStore::cast(backing_store)
                          ->is_the_hole(isolate, index)
1357 1358 1359 1360 1361
                 ? index
                 : kMaxUInt32;
    } else {
      return index < length ? index : kMaxUInt32;
    }
1362 1363
  }

1364 1365
  uint32_t GetEntryForIndex(Isolate* isolate, JSObject* holder,
                            FixedArrayBase* backing_store,
1366
                            uint32_t index) final {
1367
    return Subclass::GetEntryForIndexImpl(isolate, holder, backing_store, index,
1368
                                          ALL_PROPERTIES);
1369 1370 1371
  }

  static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
1372
                                        uint32_t entry) {
1373
    return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
1374 1375
  }

1376
  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
1377
    return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
1378 1379 1380
  }

  PropertyDetails GetDetails(JSObject* holder, uint32_t entry) final {
1381
    return Subclass::GetDetailsImpl(holder, entry);
1382 1383
  }

1384 1385 1386 1387
  Handle<FixedArray> CreateListFromArrayLike(Isolate* isolate,
                                             Handle<JSObject> object,
                                             uint32_t length) final {
    return Subclass::CreateListFromArrayLikeImpl(isolate, object, length);
1388 1389
  };

1390 1391 1392
  static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
                                                        Handle<JSObject> object,
                                                        uint32_t length) {
1393 1394 1395
    UNREACHABLE();
  }

1396 1397 1398 1399 1400
 private:
  DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase);
};


1401 1402 1403 1404 1405 1406 1407 1408
class DictionaryElementsAccessor
    : public ElementsAccessorBase<DictionaryElementsAccessor,
                                  ElementsKindTraits<DICTIONARY_ELEMENTS> > {
 public:
  explicit DictionaryElementsAccessor(const char* name)
      : ElementsAccessorBase<DictionaryElementsAccessor,
                             ElementsKindTraits<DICTIONARY_ELEMENTS> >(name) {}

1409 1410 1411 1412 1413 1414 1415
  static uint32_t GetMaxIndex(JSObject* receiver, FixedArrayBase* elements) {
    // We cannot properly estimate this for dictionaries.
    UNREACHABLE();
  }

  static uint32_t GetMaxNumberOfEntries(JSObject* receiver,
                                        FixedArrayBase* backing_store) {
1416 1417 1418 1419 1420
    return NumberOfElementsImpl(receiver, backing_store);
  }

  static uint32_t NumberOfElementsImpl(JSObject* receiver,
                                       FixedArrayBase* backing_store) {
1421
    NumberDictionary* dict = NumberDictionary::cast(backing_store);
1422
    return dict->NumberOfElements();
1423 1424
  }

1425 1426
  static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
                            uint32_t length,
1427
                            Handle<FixedArrayBase> backing_store) {
1428 1429
    Handle<NumberDictionary> dict =
        Handle<NumberDictionary>::cast(backing_store);
1430 1431 1432
    int capacity = dict->Capacity();
    uint32_t old_length = 0;
    CHECK(array->length()->ToArrayLength(&old_length));
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446
    {
      DisallowHeapAllocation no_gc;
      if (length < old_length) {
        if (dict->requires_slow_elements()) {
          // Find last non-deletable element in range of elements to be
          // deleted and adjust range accordingly.
          for (int entry = 0; entry < capacity; entry++) {
            Object* index = dict->KeyAt(entry);
            if (dict->IsKey(isolate, index)) {
              uint32_t number = static_cast<uint32_t>(index->Number());
              if (length <= number && number < old_length) {
                PropertyDetails details = dict->DetailsAt(entry);
                if (!details.IsConfigurable()) length = number + 1;
              }
1447 1448 1449 1450
            }
          }
        }

1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464
        if (length == 0) {
          // Flush the backing store.
          array->initialize_elements();
        } else {
          // Remove elements that should be deleted.
          int removed_entries = 0;
          for (int entry = 0; entry < capacity; entry++) {
            Object* index = dict->KeyAt(entry);
            if (dict->IsKey(isolate, index)) {
              uint32_t number = static_cast<uint32_t>(index->Number());
              if (length <= number && number < old_length) {
                dict->ClearEntry(entry);
                removed_entries++;
              }
1465 1466 1467
            }
          }

1468 1469 1470 1471
          if (removed_entries > 0) {
            // Update the number of elements.
            dict->ElementsRemoved(removed_entries);
          }
1472
        }
1473 1474 1475 1476 1477 1478 1479
      }
    }

    Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length);
    array->set_length(*length_obj);
  }

1480 1481 1482 1483
  static void CopyElementsImpl(Isolate* isolate, FixedArrayBase* from,
                               uint32_t from_start, FixedArrayBase* to,
                               ElementsKind from_kind, uint32_t to_start,
                               int packed_size, int copy_size) {
1484 1485 1486
    UNREACHABLE();
  }

1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
  static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
                                    uint32_t end) {
    Isolate* isolate = receiver->GetIsolate();
    uint32_t result_length = end < start ? 0u : end - start;

    // Result must also be a dictionary.
    Handle<JSArray> result_array =
        isolate->factory()->NewJSArray(0, HOLEY_ELEMENTS);
    JSObject::NormalizeElements(result_array);
    result_array->set_length(Smi::FromInt(result_length));
1497
    Handle<NumberDictionary> source_dict(
1498
        NumberDictionary::cast(receiver->elements()), isolate);
1499 1500 1501
    int entry_count = source_dict->Capacity();
    for (int i = 0; i < entry_count; i++) {
      Object* key = source_dict->KeyAt(i);
1502 1503 1504 1505
      if (!source_dict->ToKey(isolate, i, &key)) continue;
      uint64_t key_value = NumberToInt64(key);
      if (key_value >= start && key_value < end) {
        Handle<NumberDictionary> dest_dict(
1506
            NumberDictionary::cast(result_array->elements()), isolate);
1507 1508 1509 1510 1511
        Handle<Object> value(source_dict->ValueAt(i), isolate);
        PropertyDetails details = source_dict->DetailsAt(i);
        PropertyAttributes attr = details.attributes();
        AddImpl(result_array, static_cast<uint32_t>(key_value) - start, value,
                attr, 0);
1512 1513 1514 1515 1516
      }
    }

    return result_array;
  }
1517

1518
  static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
1519 1520
    Handle<NumberDictionary> dict(NumberDictionary::cast(obj->elements()),
                                  obj->GetIsolate());
1521
    dict = NumberDictionary::DeleteEntry(dict, entry);
1522
    obj->set_elements(*dict);
1523 1524
  }

1525 1526
  static bool HasAccessorsImpl(JSObject* holder,
                               FixedArrayBase* backing_store) {
1527
    DisallowHeapAllocation no_gc;
1528
    NumberDictionary* dict = NumberDictionary::cast(backing_store);
1529 1530
    if (!dict->requires_slow_elements()) return false;
    int capacity = dict->Capacity();
1531
    Isolate* isolate = holder->GetIsolate();
1532 1533
    for (int i = 0; i < capacity; i++) {
      Object* key = dict->KeyAt(i);
1534
      if (!dict->IsKey(isolate, key)) continue;
1535
      PropertyDetails details = dict->DetailsAt(i);
1536
      if (details.kind() == kAccessor) return true;
1537 1538 1539 1540
    }
    return false;
  }

1541
  static Object* GetRaw(FixedArrayBase* store, uint32_t entry) {
1542
    NumberDictionary* backing_store = NumberDictionary::cast(store);
1543 1544 1545
    return backing_store->ValueAt(entry);
  }

1546 1547 1548
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* backing_store,
                                uint32_t entry) {
    return handle(GetRaw(backing_store, entry), isolate);
1549 1550 1551
  }

  static inline void SetImpl(Handle<JSObject> holder, uint32_t entry,
1552
                             Object* value) {
1553 1554 1555 1556 1557
    SetImpl(holder->elements(), entry, value);
  }

  static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry,
                             Object* value) {
1558
    NumberDictionary::cast(backing_store)->ValueAtPut(entry, value);
1559 1560 1561
  }

  static void ReconfigureImpl(Handle<JSObject> object,
1562
                              Handle<FixedArrayBase> store, uint32_t entry,
1563 1564
                              Handle<Object> value,
                              PropertyAttributes attributes) {
1565
    NumberDictionary* dictionary = NumberDictionary::cast(*store);
1566
    if (attributes != NONE) object->RequireSlowElements(dictionary);
1567 1568
    dictionary->ValueAtPut(entry, *value);
    PropertyDetails details = dictionary->DetailsAt(entry);
1569 1570 1571
    details = PropertyDetails(kData, attributes, PropertyCellType::kNoCell,
                              details.dictionary_index());

1572
    dictionary->DetailsAtPut(entry, details);
1573 1574
  }

1575
  static void AddImpl(Handle<JSObject> object, uint32_t index,
1576 1577
                      Handle<Object> value, PropertyAttributes attributes,
                      uint32_t new_capacity) {
1578
    PropertyDetails details(kData, attributes, PropertyCellType::kNoCell);
1579
    Handle<NumberDictionary> dictionary =
1580
        object->HasFastElements() || object->HasFastStringWrapperElements()
1581
            ? JSObject::NormalizeElements(object)
1582 1583
            : handle(NumberDictionary::cast(object->elements()),
                     object->GetIsolate());
1584 1585
    Handle<NumberDictionary> new_dictionary =
        NumberDictionary::Add(dictionary, index, value, details);
1586
    new_dictionary->UpdateMaxNumberKey(index, object);
1587
    if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
1588 1589 1590 1591
    if (dictionary.is_identical_to(new_dictionary)) return;
    object->set_elements(*new_dictionary);
  }

1592 1593
  static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* store,
                           uint32_t entry) {
1594
    DisallowHeapAllocation no_gc;
1595
    NumberDictionary* dict = NumberDictionary::cast(store);
1596
    Object* index = dict->KeyAt(entry);
1597
    return !index->IsTheHole(isolate);
1598 1599
  }

1600
  static uint32_t GetIndexForEntryImpl(FixedArrayBase* store, uint32_t entry) {
1601
    DisallowHeapAllocation no_gc;
1602
    NumberDictionary* dict = NumberDictionary::cast(store);
1603
    uint32_t result = 0;
1604
    CHECK(dict->KeyAt(entry)->ToArrayIndex(&result));
1605 1606 1607
    return result;
  }

1608 1609 1610
  static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder,
                                       FixedArrayBase* store, uint32_t index,
                                       PropertyFilter filter) {
1611
    DisallowHeapAllocation no_gc;
1612
    NumberDictionary* dictionary = NumberDictionary::cast(store);
1613
    int entry = dictionary->FindEntry(isolate, index);
1614
    if (entry == NumberDictionary::kNotFound) return kMaxUInt32;
1615
    if (filter != ALL_PROPERTIES) {
1616 1617 1618 1619 1620
      PropertyDetails details = dictionary->DetailsAt(entry);
      PropertyAttributes attr = details.attributes();
      if ((attr & filter) != 0) return kMaxUInt32;
    }
    return static_cast<uint32_t>(entry);
1621 1622
  }

1623 1624 1625 1626
  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
    return GetDetailsImpl(holder->elements(), entry);
  }

1627
  static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
1628
                                        uint32_t entry) {
1629
    return NumberDictionary::cast(backing_store)->DetailsAt(entry);
1630
  }
1631

1632 1633
  static uint32_t FilterKey(Handle<NumberDictionary> dictionary, int entry,
                            Object* raw_key, PropertyFilter filter) {
1634 1635 1636 1637 1638
    DCHECK(raw_key->IsNumber());
    DCHECK_LE(raw_key->Number(), kMaxUInt32);
    PropertyDetails details = dictionary->DetailsAt(entry);
    PropertyAttributes attr = details.attributes();
    if ((attr & filter) != 0) return kMaxUInt32;
1639
    return static_cast<uint32_t>(raw_key->Number());
1640 1641
  }

1642
  static uint32_t GetKeyForEntryImpl(Isolate* isolate,
1643
                                     Handle<NumberDictionary> dictionary,
1644 1645 1646
                                     int entry, PropertyFilter filter) {
    DisallowHeapAllocation no_gc;
    Object* raw_key = dictionary->KeyAt(entry);
1647
    if (!dictionary->IsKey(isolate, raw_key)) return kMaxUInt32;
1648 1649 1650
    return FilterKey(dictionary, entry, raw_key, filter);
  }

1651 1652
  static void CollectElementIndicesImpl(Handle<JSObject> object,
                                        Handle<FixedArrayBase> backing_store,
1653 1654
                                        KeyAccumulator* keys) {
    if (keys->filter() & SKIP_STRINGS) return;
1655
    Isolate* isolate = keys->isolate();
1656 1657
    Handle<NumberDictionary> dictionary =
        Handle<NumberDictionary>::cast(backing_store);
1658
    int capacity = dictionary->Capacity();
1659 1660
    Handle<FixedArray> elements = isolate->factory()->NewFixedArray(
        GetMaxNumberOfEntries(*object, *backing_store));
1661
    int insertion_index = 0;
1662
    PropertyFilter filter = keys->filter();
1663
    for (int i = 0; i < capacity; i++) {
1664 1665 1666 1667
      Object* raw_key = dictionary->KeyAt(i);
      if (!dictionary->IsKey(isolate, raw_key)) continue;
      uint32_t key = FilterKey(dictionary, i, raw_key, filter);
      if (key == kMaxUInt32) {
1668
        keys->AddShadowingKey(raw_key);
1669 1670 1671
        continue;
      }
      elements->set(insertion_index, raw_key);
1672 1673
      insertion_index++;
    }
1674
    SortIndices(isolate, elements, insertion_index);
1675 1676
    for (int i = 0; i < insertion_index; i++) {
      keys->AddKey(elements->get(i));
1677 1678
    }
  }
1679

1680 1681 1682
  static Handle<FixedArray> DirectCollectElementIndicesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
1683 1684
      PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices,
      uint32_t insertion_index = 0) {
1685 1686
    if (filter & SKIP_STRINGS) return list;
    if (filter & ONLY_ALL_CAN_READ) return list;
1687

1688 1689
    Handle<NumberDictionary> dictionary =
        Handle<NumberDictionary>::cast(backing_store);
1690 1691
    uint32_t capacity = dictionary->Capacity();
    for (uint32_t i = 0; i < capacity; i++) {
1692
      uint32_t key = GetKeyForEntryImpl(isolate, dictionary, i, filter);
1693 1694 1695 1696 1697 1698 1699 1700 1701
      if (key == kMaxUInt32) continue;
      Handle<Object> index = isolate->factory()->NewNumberFromUint(key);
      list->set(insertion_index, *index);
      insertion_index++;
    }
    *nof_indices = insertion_index;
    return list;
  }

1702 1703 1704
  static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
                                              KeyAccumulator* accumulator,
                                              AddKeyConversion convert) {
1705
    Isolate* isolate = accumulator->isolate();
1706 1707
    Handle<NumberDictionary> dictionary(
        NumberDictionary::cast(receiver->elements()), isolate);
1708 1709 1710
    int capacity = dictionary->Capacity();
    for (int i = 0; i < capacity; i++) {
      Object* k = dictionary->KeyAt(i);
1711
      if (!dictionary->IsKey(isolate, k)) continue;
1712
      Object* value = dictionary->ValueAt(i);
1713
      DCHECK(!value->IsTheHole(isolate));
1714 1715 1716 1717 1718
      DCHECK(!value->IsAccessorPair());
      DCHECK(!value->IsAccessorInfo());
      accumulator->AddKey(value, convert);
    }
  }
1719 1720 1721 1722 1723

  static bool IncludesValueFastPath(Isolate* isolate, Handle<JSObject> receiver,
                                    Handle<Object> value, uint32_t start_from,
                                    uint32_t length, Maybe<bool>* result) {
    DisallowHeapAllocation no_gc;
1724
    NumberDictionary* dictionary = NumberDictionary::cast(receiver->elements());
1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741
    int capacity = dictionary->Capacity();
    Object* the_hole = isolate->heap()->the_hole_value();
    Object* undefined = isolate->heap()->undefined_value();

    // Scan for accessor properties. If accessors are present, then elements
    // must be accessed in order via the slow path.
    bool found = false;
    for (int i = 0; i < capacity; ++i) {
      Object* k = dictionary->KeyAt(i);
      if (k == the_hole) continue;
      if (k == undefined) continue;

      uint32_t index;
      if (!k->ToArrayIndex(&index) || index < start_from || index >= length) {
        continue;
      }

1742
      if (dictionary->DetailsAt(i).kind() == kAccessor) {
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769
        // Restart from beginning in slow path, otherwise we may observably
        // access getters out of order
        return false;
      } else if (!found) {
        Object* element_k = dictionary->ValueAt(i);
        if (value->SameValueZero(element_k)) found = true;
      }
    }

    *result = Just(found);
    return true;
  }

  static Maybe<bool> IncludesValueImpl(Isolate* isolate,
                                       Handle<JSObject> receiver,
                                       Handle<Object> value,
                                       uint32_t start_from, uint32_t length) {
    DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
    bool search_for_hole = value->IsUndefined(isolate);

    if (!search_for_hole) {
      Maybe<bool> result = Nothing<bool>();
      if (DictionaryElementsAccessor::IncludesValueFastPath(
              isolate, receiver, value, start_from, length, &result)) {
        return result;
      }
    }
1770 1771
    ElementsKind original_elements_kind = receiver->GetElementsKind();
    USE(original_elements_kind);
1772 1773
    Handle<NumberDictionary> dictionary(
        NumberDictionary::cast(receiver->elements()), isolate);
1774 1775 1776
    // Iterate through entire range, as accessing elements out of order is
    // observable
    for (uint32_t k = start_from; k < length; ++k) {
1777
      DCHECK_EQ(receiver->GetElementsKind(), original_elements_kind);
1778
      int entry = dictionary->FindEntry(isolate, k);
1779
      if (entry == NumberDictionary::kNotFound) {
1780 1781 1782 1783
        if (search_for_hole) return Just(true);
        continue;
      }

1784
      PropertyDetails details = GetDetailsImpl(*dictionary, entry);
1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
      switch (details.kind()) {
        case kData: {
          Object* element_k = dictionary->ValueAt(entry);
          if (value->SameValueZero(element_k)) return Just(true);
          break;
        }
        case kAccessor: {
          LookupIterator it(isolate, receiver, k,
                            LookupIterator::OWN_SKIP_INTERCEPTOR);
          DCHECK(it.IsFound());
          DCHECK_EQ(it.state(), LookupIterator::ACCESSOR);
          Handle<Object> element_k;

          ASSIGN_RETURN_ON_EXCEPTION_VALUE(
              isolate, element_k, JSObject::GetPropertyWithAccessor(&it),
              Nothing<bool>());

          if (value->SameValueZero(*element_k)) return Just(true);

1804
          // Bailout to slow path if elements on prototype changed
1805 1806 1807 1808
          if (!JSObject::PrototypeHasNoElements(isolate, *receiver)) {
            return IncludesValueSlowPath(isolate, receiver, value, k + 1,
                                         length);
          }
1809 1810 1811 1812 1813

          // Continue if elements unchanged
          if (*dictionary == receiver->elements()) continue;

          // Otherwise, bailout or update elements
1814 1815 1816 1817 1818 1819 1820 1821

          // If switched to initial elements, return true if searching for
          // undefined, and false otherwise.
          if (receiver->map()->GetInitialElements() == receiver->elements()) {
            return Just(search_for_hole);
          }

          // If switched to fast elements, continue with the correct accessor.
1822
          if (receiver->GetElementsKind() != DICTIONARY_ELEMENTS) {
1823 1824 1825
            ElementsAccessor* accessor = receiver->GetElementsAccessor();
            return accessor->IncludesValue(isolate, receiver, value, k + 1,
                                           length);
1826
          }
1827 1828
          dictionary =
              handle(NumberDictionary::cast(receiver->elements()), isolate);
1829 1830 1831 1832 1833 1834
          break;
        }
      }
    }
    return Just(false);
  }
1835 1836 1837 1838 1839 1840 1841

  static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
                                         Handle<JSObject> receiver,
                                         Handle<Object> value,
                                         uint32_t start_from, uint32_t length) {
    DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));

1842 1843
    ElementsKind original_elements_kind = receiver->GetElementsKind();
    USE(original_elements_kind);
1844 1845
    Handle<NumberDictionary> dictionary(
        NumberDictionary::cast(receiver->elements()), isolate);
1846 1847 1848
    // Iterate through entire range, as accessing elements out of order is
    // observable.
    for (uint32_t k = start_from; k < length; ++k) {
1849
      DCHECK_EQ(receiver->GetElementsKind(), original_elements_kind);
1850
      int entry = dictionary->FindEntry(isolate, k);
1851
      if (entry == NumberDictionary::kNotFound) continue;
1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889

      PropertyDetails details = GetDetailsImpl(*dictionary, entry);
      switch (details.kind()) {
        case kData: {
          Object* element_k = dictionary->ValueAt(entry);
          if (value->StrictEquals(element_k)) {
            return Just<int64_t>(k);
          }
          break;
        }
        case kAccessor: {
          LookupIterator it(isolate, receiver, k,
                            LookupIterator::OWN_SKIP_INTERCEPTOR);
          DCHECK(it.IsFound());
          DCHECK_EQ(it.state(), LookupIterator::ACCESSOR);
          Handle<Object> element_k;

          ASSIGN_RETURN_ON_EXCEPTION_VALUE(
              isolate, element_k, JSObject::GetPropertyWithAccessor(&it),
              Nothing<int64_t>());

          if (value->StrictEquals(*element_k)) return Just<int64_t>(k);

          // Bailout to slow path if elements on prototype changed.
          if (!JSObject::PrototypeHasNoElements(isolate, *receiver)) {
            return IndexOfValueSlowPath(isolate, receiver, value, k + 1,
                                        length);
          }

          // Continue if elements unchanged.
          if (*dictionary == receiver->elements()) continue;

          // Otherwise, bailout or update elements.
          if (receiver->GetElementsKind() != DICTIONARY_ELEMENTS) {
            // Otherwise, switch to slow path.
            return IndexOfValueSlowPath(isolate, receiver, value, k + 1,
                                        length);
          }
1890 1891
          dictionary =
              handle(NumberDictionary::cast(receiver->elements()), isolate);
1892 1893 1894 1895 1896 1897
          break;
        }
      }
    }
    return Just<int64_t>(-1);
  }
1898 1899 1900 1901 1902 1903 1904

  static void ValidateContents(JSObject* holder, int length) {
    DisallowHeapAllocation no_gc;
#if DEBUG
    DCHECK_EQ(holder->map()->elements_kind(), DICTIONARY_ELEMENTS);
    if (!FLAG_enable_slow_asserts) return;
    Isolate* isolate = holder->GetIsolate();
1905
    NumberDictionary* dictionary = NumberDictionary::cast(holder->elements());
1906 1907 1908 1909 1910 1911 1912 1913
    // Validate the requires_slow_elements and max_number_key values.
    int capacity = dictionary->Capacity();
    bool requires_slow_elements = false;
    int max_key = 0;
    for (int i = 0; i < capacity; ++i) {
      Object* k;
      if (!dictionary->ToKey(isolate, i, &k)) continue;
      DCHECK_LE(0.0, k->Number());
1914
      if (k->Number() > NumberDictionary::kRequiresSlowElementsLimit) {
1915 1916
        requires_slow_elements = true;
      } else {
jgruber's avatar
jgruber committed
1917
        max_key = Max(max_key, Smi::ToInt(k));
1918 1919 1920 1921 1922 1923 1924 1925 1926
      }
    }
    if (requires_slow_elements) {
      DCHECK(dictionary->requires_slow_elements());
    } else if (!dictionary->requires_slow_elements()) {
      DCHECK_LE(max_key, dictionary->max_number_key());
    }
#endif
  }
1927 1928
};

1929

1930
// Super class for all fast element arrays.
1931 1932
template <typename Subclass, typename KindTraits>
class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
1933 1934
 public:
  explicit FastElementsAccessor(const char* name)
1935
      : ElementsAccessorBase<Subclass, KindTraits>(name) {}
1936

1937
  typedef typename KindTraits::BackingStore BackingStore;
1938

1939 1940
  static Handle<NumberDictionary> NormalizeImpl(Handle<JSObject> object,
                                                Handle<FixedArrayBase> store) {
1941
    Isolate* isolate = object->GetIsolate();
1942
    ElementsKind kind = Subclass::kind();
1943 1944 1945

    // Ensure that notifications fire if the array or object prototypes are
    // normalizing.
1946 1947
    if (IsSmiOrObjectElementsKind(kind) ||
        kind == FAST_STRING_WRAPPER_ELEMENTS) {
1948
      isolate->UpdateNoElementsProtectorOnNormalizeElements(object);
1949 1950 1951
    }

    int capacity = object->GetFastElementsUsage();
1952 1953
    Handle<NumberDictionary> dictionary =
        NumberDictionary::New(isolate, capacity);
1954 1955 1956

    PropertyDetails details = PropertyDetails::Empty();
    int j = 0;
1957
    int max_number_key = -1;
1958
    for (int i = 0; j < capacity; i++) {
1959
      if (IsHoleyOrDictionaryElementsKind(kind)) {
1960
        if (BackingStore::cast(*store)->is_the_hole(isolate, i)) continue;
1961
      }
1962
      max_number_key = i;
1963
      Handle<Object> value = Subclass::GetImpl(isolate, *store, i);
1964
      dictionary = NumberDictionary::Add(dictionary, i, value, details);
1965 1966
      j++;
    }
1967 1968 1969 1970 1971

    if (max_number_key > 0) {
      dictionary->UpdateMaxNumberKey(static_cast<uint32_t>(max_number_key),
                                     object);
    }
1972 1973 1974
    return dictionary;
  }

1975 1976 1977
  static void DeleteAtEnd(Handle<JSObject> obj,
                          Handle<BackingStore> backing_store, uint32_t entry) {
    uint32_t length = static_cast<uint32_t>(backing_store->length());
1978
    Isolate* isolate = obj->GetIsolate();
1979
    for (; entry > 0; entry--) {
1980
      if (!backing_store->is_the_hole(isolate, entry - 1)) break;
1981 1982
    }
    if (entry == 0) {
1983
      FixedArray* empty = isolate->heap()->empty_fixed_array();
1984 1985 1986
      // Dynamically ask for the elements kind here since we manually redirect
      // the operations for argument backing stores.
      if (obj->GetElementsKind() == FAST_SLOPPY_ARGUMENTS_ELEMENTS) {
1987
        SloppyArgumentsElements::cast(obj->elements())->set_arguments(empty);
1988 1989 1990 1991 1992 1993
      } else {
        obj->set_elements(empty);
      }
      return;
    }

1994
    isolate->heap()->RightTrimFixedArray(*backing_store, length - entry);
1995 1996
  }

1997
  static void DeleteCommon(Handle<JSObject> obj, uint32_t entry,
1998
                           Handle<FixedArrayBase> store) {
1999
    DCHECK(obj->HasSmiOrObjectElements() || obj->HasDoubleElements() ||
2000 2001
           obj->HasFastArgumentsElements() ||
           obj->HasFastStringWrapperElements());
2002
    Handle<BackingStore> backing_store = Handle<BackingStore>::cast(store);
2003 2004 2005 2006 2007 2008
    if (!obj->IsJSArray() &&
        entry == static_cast<uint32_t>(store->length()) - 1) {
      DeleteAtEnd(obj, backing_store, entry);
      return;
    }

2009
    Isolate* isolate = obj->GetIsolate();
2010
    backing_store->set_the_hole(isolate, entry);
2011 2012 2013 2014 2015 2016

    // TODO(verwaest): Move this out of elements.cc.
    // If an old space backing store is larger than a certain size and
    // has too few used values, normalize it.
    const int kMinLengthForSparsenessCheck = 64;
    if (backing_store->length() < kMinLengthForSparsenessCheck) return;
2017
    if (isolate->heap()->InNewSpace(*backing_store)) return;
2018 2019 2020 2021 2022
    uint32_t length = 0;
    if (obj->IsJSArray()) {
      JSArray::cast(*obj)->length()->ToArrayLength(&length);
    } else {
      length = static_cast<uint32_t>(store->length());
2023
    }
2024 2025 2026 2027 2028 2029 2030 2031

    // To avoid doing the check on every delete, use a counter-based heuristic.
    const int kLengthFraction = 16;
    // The above constant must be large enough to ensure that we check for
    // normalization frequently enough. At a minimum, it should be large
    // enough to reliably hit the "window" of remaining elements count where
    // normalization would be beneficial.
    STATIC_ASSERT(kLengthFraction >=
2032 2033
                  NumberDictionary::kEntrySize *
                      NumberDictionary::kPreferFastElementsSizeFactor);
2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045
    size_t current_counter = isolate->elements_deletion_counter();
    if (current_counter < length / kLengthFraction) {
      isolate->set_elements_deletion_counter(current_counter + 1);
      return;
    }
    // Reset the counter whenever the full check is performed.
    isolate->set_elements_deletion_counter(0);

    if (!obj->IsJSArray()) {
      uint32_t i;
      for (i = entry + 1; i < length; i++) {
        if (!backing_store->is_the_hole(isolate, i)) break;
2046
      }
2047 2048 2049 2050 2051 2052 2053 2054 2055 2056
      if (i == length) {
        DeleteAtEnd(obj, backing_store, entry);
        return;
      }
    }
    int num_used = 0;
    for (int i = 0; i < backing_store->length(); ++i) {
      if (!backing_store->is_the_hole(isolate, i)) {
        ++num_used;
        // Bail out if a number dictionary wouldn't be able to save much space.
2057 2058 2059
        if (NumberDictionary::kPreferFastElementsSizeFactor *
                NumberDictionary::ComputeCapacity(num_used) *
                NumberDictionary::kEntrySize >
2060 2061
            static_cast<uint32_t>(backing_store->length())) {
          return;
2062
        }
2063 2064
      }
    }
2065
    JSObject::NormalizeElements(obj);
2066 2067
  }

2068
  static void ReconfigureImpl(Handle<JSObject> object,
2069
                              Handle<FixedArrayBase> store, uint32_t entry,
2070 2071
                              Handle<Object> value,
                              PropertyAttributes attributes) {
2072
    Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(object);
2073 2074
    entry = dictionary->FindEntry(entry);
    DictionaryElementsAccessor::ReconfigureImpl(object, dictionary, entry,
2075
                                                value, attributes);
2076 2077
  }

2078
  static void AddImpl(Handle<JSObject> object, uint32_t index,
2079 2080 2081 2082
                      Handle<Object> value, PropertyAttributes attributes,
                      uint32_t new_capacity) {
    DCHECK_EQ(NONE, attributes);
    ElementsKind from_kind = object->GetElementsKind();
2083
    ElementsKind to_kind = Subclass::kind();
2084
    if (IsDictionaryElementsKind(from_kind) ||
2085
        IsDoubleElementsKind(from_kind) != IsDoubleElementsKind(to_kind) ||
2086 2087 2088
        Subclass::GetCapacityImpl(*object, object->elements()) !=
            new_capacity) {
      Subclass::GrowCapacityAndConvertImpl(object, new_capacity);
2089
    } else {
2090
      if (IsFastElementsKind(from_kind) && from_kind != to_kind) {
2091 2092
        JSObject::TransitionElementsKind(object, to_kind);
      }
2093 2094
      if (IsSmiOrObjectElementsKind(from_kind)) {
        DCHECK(IsSmiOrObjectElementsKind(to_kind));
2095 2096 2097
        JSObject::EnsureWritableFastElements(object);
      }
    }
2098
    Subclass::SetImpl(object, index, *value);
2099 2100
  }

2101
  static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
2102 2103 2104 2105
    ElementsKind kind = KindTraits::Kind;
    if (IsFastPackedElementsKind(kind)) {
      JSObject::TransitionElementsKind(obj, GetHoleyElementsKind(kind));
    }
2106
    if (IsSmiOrObjectElementsKind(KindTraits::Kind)) {
2107 2108
      JSObject::EnsureWritableFastElements(obj);
    }
2109
    DeleteCommon(obj, entry, handle(obj->elements(), obj->GetIsolate()));
2110 2111
  }

2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126
  static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* backing_store,
                           uint32_t entry) {
    return !BackingStore::cast(backing_store)->is_the_hole(isolate, entry);
  }

  static uint32_t NumberOfElementsImpl(JSObject* receiver,
                                       FixedArrayBase* backing_store) {
    uint32_t max_index = Subclass::GetMaxIndex(receiver, backing_store);
    if (IsFastPackedElementsKind(Subclass::kind())) return max_index;
    Isolate* isolate = receiver->GetIsolate();
    uint32_t count = 0;
    for (uint32_t i = 0; i < max_index; i++) {
      if (Subclass::HasEntryImpl(isolate, backing_store, i)) count++;
    }
    return count;
2127 2128
  }

2129 2130 2131
  static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
                                              KeyAccumulator* accumulator,
                                              AddKeyConversion convert) {
2132 2133
    Isolate* isolate = accumulator->isolate();
    Handle<FixedArrayBase> elements(receiver->elements(), isolate);
2134
    uint32_t length = Subclass::GetMaxNumberOfEntries(*receiver, *elements);
2135 2136
    for (uint32_t i = 0; i < length; i++) {
      if (IsFastPackedElementsKind(KindTraits::Kind) ||
2137
          HasEntryImpl(isolate, *elements, i)) {
2138
        accumulator->AddKey(Subclass::GetImpl(isolate, *elements, i), convert);
2139 2140 2141 2142
      }
    }
  }

2143
  static void ValidateContents(JSObject* holder, int length) {
2144
#if DEBUG
2145
    Isolate* isolate = holder->GetIsolate();
2146
    Heap* heap = isolate->heap();
2147
    FixedArrayBase* elements = holder->elements();
2148
    Map* map = elements->map();
2149
    if (IsSmiOrObjectElementsKind(KindTraits::Kind)) {
2150
      DCHECK_NE(map, heap->fixed_double_array_map());
2151
    } else if (IsDoubleElementsKind(KindTraits::Kind)) {
2152 2153 2154 2155 2156
      DCHECK_NE(map, heap->fixed_cow_array_map());
      if (map == heap->fixed_array_map()) DCHECK_EQ(0, length);
    } else {
      UNREACHABLE();
    }
2157
    if (length == 0) return;  // nothing to do!
2158
#if ENABLE_SLOW_DCHECKS
2159
    DisallowHeapAllocation no_gc;
2160
    BackingStore* backing_store = BackingStore::cast(elements);
2161
    if (IsSmiElementsKind(KindTraits::Kind)) {
2162
      HandleScope scope(isolate);
2163
      for (int i = 0; i < length; i++) {
2164
        DCHECK(BackingStore::get(backing_store, i, isolate)->IsSmi() ||
2165
               (IsHoleyElementsKind(KindTraits::Kind) &&
2166
                backing_store->is_the_hole(isolate, i)));
2167
      }
2168 2169
    } else if (KindTraits::Kind == PACKED_ELEMENTS ||
               KindTraits::Kind == PACKED_DOUBLE_ELEMENTS) {
2170
      for (int i = 0; i < length; i++) {
2171
        DCHECK(!backing_store->is_the_hole(isolate, i));
2172 2173
      }
    } else {
2174
      DCHECK(IsHoleyElementsKind(KindTraits::Kind));
2175
    }
2176
#endif
2177 2178
#endif
  }
2179

2180
  static Handle<Object> PopImpl(Handle<JSArray> receiver) {
2181
    return Subclass::RemoveElement(receiver, AT_END);
2182 2183
  }

2184
  static Handle<Object> ShiftImpl(Handle<JSArray> receiver) {
2185
    return Subclass::RemoveElement(receiver, AT_START);
cbruni's avatar
cbruni committed
2186 2187
  }

2188
  static uint32_t PushImpl(Handle<JSArray> receiver,
2189
                           Arguments* args, uint32_t push_size) {
2190 2191
    Handle<FixedArrayBase> backing_store(receiver->elements(),
                                         receiver->GetIsolate());
2192 2193
    return Subclass::AddArguments(receiver, backing_store, args, push_size,
                                  AT_END);
2194
  }
2195

2196 2197
  static uint32_t UnshiftImpl(Handle<JSArray> receiver,
                              Arguments* args, uint32_t unshift_size) {
2198 2199
    Handle<FixedArrayBase> backing_store(receiver->elements(),
                                         receiver->GetIsolate());
2200 2201
    return Subclass::AddArguments(receiver, backing_store, args, unshift_size,
                                  AT_START);
2202 2203
  }

2204 2205
  static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
                                    uint32_t end) {
2206
    Isolate* isolate = receiver->GetIsolate();
2207 2208
    Handle<FixedArrayBase> backing_store(receiver->elements(), isolate);
    int result_len = end < start ? 0u : end - start;
2209 2210 2211
    Handle<JSArray> result_array = isolate->factory()->NewJSArray(
        KindTraits::Kind, result_len, result_len);
    DisallowHeapAllocation no_gc;
2212 2213 2214
    Subclass::CopyElementsImpl(isolate, *backing_store, start,
                               result_array->elements(), KindTraits::Kind, 0,
                               kPackedSizeNotKnown, result_len);
2215
    Subclass::TryTransitionResultArrayToPacked(result_array);
2216 2217 2218
    return result_array;
  }

2219 2220
  static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver,
                                    uint32_t start, uint32_t delete_count,
2221
                                    Arguments* args, uint32_t add_count) {
2222 2223
    Isolate* isolate = receiver->GetIsolate();
    Heap* heap = isolate->heap();
jgruber's avatar
jgruber committed
2224
    uint32_t length = Smi::ToInt(receiver->length());
cbruni's avatar
cbruni committed
2225
    uint32_t new_length = length - delete_count + add_count;
2226

2227 2228
    ElementsKind kind = KindTraits::Kind;
    if (new_length <= static_cast<uint32_t>(receiver->elements()->length()) &&
2229
        IsSmiOrObjectElementsKind(kind)) {
2230 2231 2232 2233 2234 2235
      HandleScope scope(isolate);
      JSObject::EnsureWritableFastElements(receiver);
    }

    Handle<FixedArrayBase> backing_store(receiver->elements(), isolate);

2236 2237
    if (new_length == 0) {
      receiver->set_elements(heap->empty_fixed_array());
2238
      receiver->set_length(Smi::kZero);
2239 2240 2241 2242
      return isolate->factory()->NewJSArrayWithElements(
          backing_store, KindTraits::Kind, delete_count);
    }

cbruni's avatar
cbruni committed
2243
    // Construct the result array which holds the deleted elements.
2244 2245 2246 2247
    Handle<JSArray> deleted_elements = isolate->factory()->NewJSArray(
        KindTraits::Kind, delete_count, delete_count);
    if (delete_count > 0) {
      DisallowHeapAllocation no_gc;
2248
      Subclass::CopyElementsImpl(isolate, *backing_store, start,
2249 2250
                                 deleted_elements->elements(), KindTraits::Kind,
                                 0, kPackedSizeNotKnown, delete_count);
2251 2252
    }

cbruni's avatar
cbruni committed
2253
    // Delete and move elements to make space for add_count new elements.
2254
    if (add_count < delete_count) {
2255 2256
      Subclass::SpliceShrinkStep(isolate, receiver, backing_store, start,
                                 delete_count, add_count, length, new_length);
2257
    } else if (add_count > delete_count) {
2258 2259 2260
      backing_store =
          Subclass::SpliceGrowStep(isolate, receiver, backing_store, start,
                                   delete_count, add_count, length, new_length);
2261 2262
    }

cbruni's avatar
cbruni committed
2263
    // Copy over the arguments.
2264
    Subclass::CopyArguments(args, backing_store, add_count, 3, start);
2265 2266

    receiver->set_length(Smi::FromInt(new_length));
2267
    Subclass::TryTransitionResultArrayToPacked(deleted_elements);
2268 2269 2270
    return deleted_elements;
  }

2271 2272 2273 2274 2275 2276
  static void MoveElements(Isolate* isolate, Handle<JSArray> receiver,
                           Handle<FixedArrayBase> backing_store, int dst_index,
                           int src_index, int len, int hole_start,
                           int hole_end) {
    Heap* heap = isolate->heap();
    Handle<BackingStore> dst_elms = Handle<BackingStore>::cast(backing_store);
2277 2278
    if (len > JSArray::kMaxCopyElements && dst_index == 0 &&
        heap->CanMoveObjectStart(*dst_elms)) {
2279 2280 2281 2282 2283 2284 2285 2286 2287
      // Update all the copies of this backing_store handle.
      *dst_elms.location() =
          BackingStore::cast(heap->LeftTrimFixedArray(*dst_elms, src_index));
      receiver->set_elements(*dst_elms);
      // Adjust the hole offset as the array has been shrunk.
      hole_end -= src_index;
      DCHECK_LE(hole_start, backing_store->length());
      DCHECK_LE(hole_end, backing_store->length());
    } else if (len != 0) {
2288
      if (IsDoubleElementsKind(KindTraits::Kind)) {
2289 2290 2291 2292
        MemMove(dst_elms->data_start() + dst_index,
                dst_elms->data_start() + src_index, len * kDoubleSize);
      } else {
        DisallowHeapAllocation no_gc;
2293
        WriteBarrierMode mode = GetWriteBarrierMode(KindTraits::Kind);
2294
        heap->MoveElements(FixedArray::cast(*dst_elms), dst_index, src_index,
2295
                           len, mode);
2296 2297 2298 2299 2300 2301 2302
      }
    }
    if (hole_start != hole_end) {
      dst_elms->FillWithHoles(hole_start, hole_end);
    }
  }

2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325
  static Maybe<bool> IncludesValueImpl(Isolate* isolate,
                                       Handle<JSObject> receiver,
                                       Handle<Object> search_value,
                                       uint32_t start_from, uint32_t length) {
    DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
    DisallowHeapAllocation no_gc;
    FixedArrayBase* elements_base = receiver->elements();
    Object* the_hole = isolate->heap()->the_hole_value();
    Object* undefined = isolate->heap()->undefined_value();
    Object* value = *search_value;

    // Elements beyond the capacity of the backing store treated as undefined.
    if (value == undefined &&
        static_cast<uint32_t>(elements_base->length()) < length) {
      return Just(true);
    }

    if (start_from >= length) return Just(false);

    length = std::min(static_cast<uint32_t>(elements_base->length()), length);

    if (!value->IsNumber()) {
      if (value == undefined) {
2326 2327
        // Only PACKED_ELEMENTS, HOLEY_ELEMENTS, HOLEY_SMI_ELEMENTS, and
        // HOLEY_DOUBLE_ELEMENTS can have `undefined` as a value.
2328 2329
        if (!IsObjectElementsKind(Subclass::kind()) &&
            !IsHoleyElementsKind(Subclass::kind())) {
2330 2331 2332
          return Just(false);
        }

2333 2334
        // Search for `undefined` or The Hole in PACKED_ELEMENTS,
        // HOLEY_ELEMENTS or HOLEY_SMI_ELEMENTS
2335
        if (IsSmiOrObjectElementsKind(Subclass::kind())) {
2336 2337 2338 2339 2340
          auto elements = FixedArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
            Object* element_k = elements->get(k);

2341
            if (IsHoleyElementsKind(Subclass::kind()) &&
2342 2343 2344
                element_k == the_hole) {
              return Just(true);
            }
2345
            if (IsObjectElementsKind(Subclass::kind()) &&
2346 2347 2348 2349 2350 2351
                element_k == undefined) {
              return Just(true);
            }
          }
          return Just(false);
        } else {
2352
          // Search for The Hole in HOLEY_DOUBLE_ELEMENTS
2353
          DCHECK_EQ(Subclass::kind(), HOLEY_DOUBLE_ELEMENTS);
2354 2355 2356
          auto elements = FixedDoubleArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
2357
            if (IsHoleyElementsKind(Subclass::kind()) &&
2358 2359 2360 2361 2362 2363
                elements->is_the_hole(k)) {
              return Just(true);
            }
          }
          return Just(false);
        }
2364
      } else if (!IsObjectElementsKind(Subclass::kind())) {
2365
        // Search for non-number, non-Undefined value, with either
2366 2367
        // PACKED_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, HOLEY_SMI_ELEMENTS or
        // HOLEY_DOUBLE_ELEMENTS. Guaranteed to return false, since these
2368 2369 2370 2371
        // elements kinds can only contain Number values or undefined.
        return Just(false);
      } else {
        // Search for non-number, non-Undefined value with either
2372
        // PACKED_ELEMENTS or HOLEY_ELEMENTS.
2373
        DCHECK(IsObjectElementsKind(Subclass::kind()));
2374 2375 2376 2377
        auto elements = FixedArray::cast(receiver->elements());

        for (uint32_t k = start_from; k < length; ++k) {
          Object* element_k = elements->get(k);
2378
          if (IsHoleyElementsKind(Subclass::kind()) && element_k == the_hole) {
2379 2380 2381 2382 2383 2384 2385 2386 2387 2388
            continue;
          }

          if (value->SameValueZero(element_k)) return Just(true);
        }
        return Just(false);
      }
    } else {
      if (!value->IsNaN()) {
        double search_value = value->Number();
2389
        if (IsDoubleElementsKind(Subclass::kind())) {
2390 2391
          // Search for non-NaN Number in PACKED_DOUBLE_ELEMENTS or
          // HOLEY_DOUBLE_ELEMENTS --- Skip TheHole, and trust UCOMISD or
2392 2393 2394 2395
          // similar operation for result.
          auto elements = FixedDoubleArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
2396
            if (IsHoleyElementsKind(Subclass::kind()) &&
2397 2398 2399 2400 2401 2402 2403
                elements->is_the_hole(k)) {
              continue;
            }
            if (elements->get_scalar(k) == search_value) return Just(true);
          }
          return Just(false);
        } else {
2404 2405
          // Search for non-NaN Number in PACKED_ELEMENTS, HOLEY_ELEMENTS,
          // PACKED_SMI_ELEMENTS or HOLEY_SMI_ELEMENTS --- Skip non-Numbers,
2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418
          // and trust UCOMISD or similar operation for result
          auto elements = FixedArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
            Object* element_k = elements->get(k);
            if (element_k->IsNumber() && element_k->Number() == search_value) {
              return Just(true);
            }
          }
          return Just(false);
        }
      } else {
        // Search for NaN --- NaN cannot be represented with Smi elements, so
2419
        // abort if ElementsKind is PACKED_SMI_ELEMENTS or HOLEY_SMI_ELEMENTS
2420
        if (IsSmiElementsKind(Subclass::kind())) return Just(false);
2421

2422
        if (IsDoubleElementsKind(Subclass::kind())) {
2423 2424
          // Search for NaN in PACKED_DOUBLE_ELEMENTS or
          // HOLEY_DOUBLE_ELEMENTS --- Skip The Hole and trust
2425 2426 2427 2428
          // std::isnan(elementK) for result
          auto elements = FixedDoubleArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
2429
            if (IsHoleyElementsKind(Subclass::kind()) &&
2430 2431 2432 2433 2434 2435 2436
                elements->is_the_hole(k)) {
              continue;
            }
            if (std::isnan(elements->get_scalar(k))) return Just(true);
          }
          return Just(false);
        } else {
2437 2438
          // Search for NaN in PACKED_ELEMENTS, HOLEY_ELEMENTS,
          // PACKED_SMI_ELEMENTS or HOLEY_SMI_ELEMENTS. Return true if
2439
          // elementK->IsHeapNumber() && std::isnan(elementK->Number())
2440
          DCHECK(IsSmiOrObjectElementsKind(Subclass::kind()));
2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451
          auto elements = FixedArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
            if (elements->get(k)->IsNaN()) return Just(true);
          }
          return Just(false);
        }
      }
    }
  }

2452 2453 2454
  static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
                                                        Handle<JSObject> object,
                                                        uint32_t length) {
2455
    Handle<FixedArray> result = isolate->factory()->NewFixedArray(length);
2456
    Handle<FixedArrayBase> elements(object->elements(), isolate);
2457
    for (uint32_t i = 0; i < length; i++) {
2458
      if (!Subclass::HasElementImpl(isolate, *object, i, *elements)) continue;
2459 2460 2461 2462 2463 2464 2465 2466 2467 2468
      Handle<Object> value;
      value = Subclass::GetImpl(isolate, *elements, i);
      if (value->IsName()) {
        value = isolate->factory()->InternalizeName(Handle<Name>::cast(value));
      }
      result->set(i, *value);
    }
    return result;
  }

2469
 private:
2470 2471 2472
  // SpliceShrinkStep might modify the backing_store.
  static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver,
                               Handle<FixedArrayBase> backing_store,
cbruni's avatar
cbruni committed
2473 2474 2475
                               uint32_t start, uint32_t delete_count,
                               uint32_t add_count, uint32_t len,
                               uint32_t new_length) {
2476 2477
    const int move_left_count = len - delete_count - start;
    const int move_left_dst_index = start + add_count;
2478 2479 2480
    Subclass::MoveElements(isolate, receiver, backing_store,
                           move_left_dst_index, start + delete_count,
                           move_left_count, new_length, len);
cbruni's avatar
cbruni committed
2481 2482
  }

2483
  // SpliceGrowStep might modify the backing_store.
cbruni's avatar
cbruni committed
2484
  static Handle<FixedArrayBase> SpliceGrowStep(
2485 2486 2487 2488
      Isolate* isolate, Handle<JSArray> receiver,
      Handle<FixedArrayBase> backing_store, uint32_t start,
      uint32_t delete_count, uint32_t add_count, uint32_t length,
      uint32_t new_length) {
cbruni's avatar
cbruni committed
2489 2490 2491 2492
    // Check we do not overflow the new_length.
    DCHECK((add_count - delete_count) <= (Smi::kMaxValue - length));
    // Check if backing_store is big enough.
    if (new_length <= static_cast<uint32_t>(backing_store->length())) {
2493 2494 2495
      Subclass::MoveElements(isolate, receiver, backing_store,
                             start + add_count, start + delete_count,
                             (length - delete_count - start), 0, 0);
2496
      // MoveElements updates the backing_store in-place.
cbruni's avatar
cbruni committed
2497
      return backing_store;
2498
    }
cbruni's avatar
cbruni committed
2499 2500 2501
    // New backing storage is needed.
    int capacity = JSObject::NewElementsCapacity(new_length);
    // Partially copy all elements up to start.
2502 2503
    Handle<FixedArrayBase> new_elms = Subclass::ConvertElementsWithCapacity(
        receiver, backing_store, KindTraits::Kind, capacity, start);
cbruni's avatar
cbruni committed
2504
    // Copy the trailing elements after start + delete_count
2505 2506
    Subclass::CopyElementsImpl(isolate, *backing_store, start + delete_count,
                               *new_elms, KindTraits::Kind, start + add_count,
2507 2508
                               kPackedSizeNotKnown,
                               ElementsAccessor::kCopyToEndAndInitializeToHole);
cbruni's avatar
cbruni committed
2509 2510
    receiver->set_elements(*new_elms);
    return new_elms;
2511 2512
  }

cbruni's avatar
cbruni committed
2513 2514
  static Handle<Object> RemoveElement(Handle<JSArray> receiver,
                                      Where remove_position) {
2515
    Isolate* isolate = receiver->GetIsolate();
2516
    ElementsKind kind = KindTraits::Kind;
2517
    if (IsSmiOrObjectElementsKind(kind)) {
2518 2519 2520 2521
      HandleScope scope(isolate);
      JSObject::EnsureWritableFastElements(receiver);
    }
    Handle<FixedArrayBase> backing_store(receiver->elements(), isolate);
jgruber's avatar
jgruber committed
2522
    uint32_t length = static_cast<uint32_t>(Smi::ToInt(receiver->length()));
2523
    DCHECK_GT(length, 0);
cbruni's avatar
cbruni committed
2524 2525
    int new_length = length - 1;
    int remove_index = remove_position == AT_START ? 0 : new_length;
2526 2527
    Handle<Object> result =
        Subclass::GetImpl(isolate, *backing_store, remove_index);
cbruni's avatar
cbruni committed
2528
    if (remove_position == AT_START) {
2529 2530
      Subclass::MoveElements(isolate, receiver, backing_store, 0, 1, new_length,
                             0, 0);
cbruni's avatar
cbruni committed
2531
    }
2532
    Subclass::SetLengthImpl(isolate, receiver, new_length, backing_store);
2533

2534
    if (IsHoleyOrDictionaryElementsKind(kind) && result->IsTheHole(isolate)) {
2535
      return isolate->factory()->undefined_value();
cbruni's avatar
cbruni committed
2536 2537 2538 2539 2540 2541 2542
    }
    return result;
  }

  static uint32_t AddArguments(Handle<JSArray> receiver,
                               Handle<FixedArrayBase> backing_store,
                               Arguments* args, uint32_t add_size,
2543
                               Where add_position) {
jgruber's avatar
jgruber committed
2544
    uint32_t length = Smi::ToInt(receiver->length());
2545
    DCHECK_LT(0, add_size);
cbruni's avatar
cbruni committed
2546 2547 2548 2549 2550 2551
    uint32_t elms_len = backing_store->length();
    // Check we do not overflow the new_length.
    DCHECK(add_size <= static_cast<uint32_t>(Smi::kMaxValue - length));
    uint32_t new_length = length + add_size;

    if (new_length > elms_len) {
2552
      // New backing storage is needed.
cbruni's avatar
cbruni committed
2553 2554
      uint32_t capacity = JSObject::NewElementsCapacity(new_length);
      // If we add arguments to the start we have to shift the existing objects.
2555
      int copy_dst_index = add_position == AT_START ? add_size : 0;
cbruni's avatar
cbruni committed
2556
      // Copy over all objects to a new backing_store.
2557
      backing_store = Subclass::ConvertElementsWithCapacity(
cbruni's avatar
cbruni committed
2558 2559 2560
          receiver, backing_store, KindTraits::Kind, capacity, 0,
          copy_dst_index, ElementsAccessor::kCopyToEndAndInitializeToHole);
      receiver->set_elements(*backing_store);
2561
    } else if (add_position == AT_START) {
cbruni's avatar
cbruni committed
2562 2563 2564
      // If the backing store has enough capacity and we add elements to the
      // start we have to shift the existing objects.
      Isolate* isolate = receiver->GetIsolate();
2565 2566
      Subclass::MoveElements(isolate, receiver, backing_store, add_size, 0,
                             length, 0, 0);
cbruni's avatar
cbruni committed
2567
    }
2568

2569
    int insertion_index = add_position == AT_START ? 0 : length;
cbruni's avatar
cbruni committed
2570
    // Copy the arguments to the start.
2571
    Subclass::CopyArguments(args, backing_store, add_size, 1, insertion_index);
cbruni's avatar
cbruni committed
2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584
    // Set the length.
    receiver->set_length(Smi::FromInt(new_length));
    return new_length;
  }

  static void CopyArguments(Arguments* args, Handle<FixedArrayBase> dst_store,
                            uint32_t copy_size, uint32_t src_index,
                            uint32_t dst_index) {
    // Add the provided values.
    DisallowHeapAllocation no_gc;
    FixedArrayBase* raw_backing_store = *dst_store;
    WriteBarrierMode mode = raw_backing_store->GetWriteBarrierMode(no_gc);
    for (uint32_t i = 0; i < copy_size; i++) {
2585
      Object* argument = (*args)[src_index + i];
2586
      DCHECK(!argument->IsTheHole());
2587
      Subclass::SetImpl(raw_backing_store, dst_index + i, argument, mode);
2588 2589
    }
  }
2590 2591
};

2592
template <typename Subclass, typename KindTraits>
2593
class FastSmiOrObjectElementsAccessor
2594
    : public FastElementsAccessor<Subclass, KindTraits> {
2595 2596
 public:
  explicit FastSmiOrObjectElementsAccessor(const char* name)
2597
      : FastElementsAccessor<Subclass, KindTraits>(name) {}
2598

2599 2600 2601 2602 2603
  static inline void SetImpl(Handle<JSObject> holder, uint32_t entry,
                             Object* value) {
    SetImpl(holder->elements(), entry, value);
  }

2604 2605 2606 2607 2608 2609 2610 2611 2612 2613
  static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry,
                             Object* value) {
    FixedArray::cast(backing_store)->set(entry, value);
  }

  static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry,
                             Object* value, WriteBarrierMode mode) {
    FixedArray::cast(backing_store)->set(entry, value, mode);
  }

2614
  static Object* GetRaw(FixedArray* backing_store, uint32_t entry) {
2615
    uint32_t index = Subclass::GetIndexForEntryImpl(backing_store, entry);
2616 2617 2618
    return backing_store->get(index);
  }

2619 2620 2621 2622 2623
  // NOTE: this method violates the handlified function signature convention:
  // raw pointer parameters in the function that allocates.
  // See ElementsAccessor::CopyElements() for details.
  // This method could actually allocate if copying from double elements to
  // object elements.
2624 2625 2626 2627
  static void CopyElementsImpl(Isolate* isolate, FixedArrayBase* from,
                               uint32_t from_start, FixedArrayBase* to,
                               ElementsKind from_kind, uint32_t to_start,
                               int packed_size, int copy_size) {
2628
    DisallowHeapAllocation no_gc;
2629 2630
    ElementsKind to_kind = KindTraits::Kind;
    switch (from_kind) {
2631 2632 2633 2634
      case PACKED_SMI_ELEMENTS:
      case HOLEY_SMI_ELEMENTS:
      case PACKED_ELEMENTS:
      case HOLEY_ELEMENTS:
2635 2636
        CopyObjectToObjectElements(isolate, from, from_kind, from_start, to,
                                   to_kind, to_start, copy_size);
2637
        break;
2638 2639
      case PACKED_DOUBLE_ELEMENTS:
      case HOLEY_DOUBLE_ELEMENTS: {
2640
        AllowHeapAllocation allow_allocation;
2641
        DCHECK(IsObjectElementsKind(to_kind));
2642 2643
        CopyDoubleToObjectElements(isolate, from, from_start, to, to_start,
                                   copy_size);
2644
        break;
2645
      }
2646
      case DICTIONARY_ELEMENTS:
2647 2648
        CopyDictionaryToObjectElements(isolate, from, from_start, to, to_kind,
                                       to_start, copy_size);
2649
        break;
2650 2651
      case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
      case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
2652 2653
      case FAST_STRING_WRAPPER_ELEMENTS:
      case SLOW_STRING_WRAPPER_ELEMENTS:
2654
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
2655 2656
      TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
2657 2658 2659 2660 2661 2662
      // This function is currently only used for JSArrays with non-zero
      // length.
      UNREACHABLE();
      break;
      case NO_ELEMENTS:
        break;  // Nothing to do.
2663 2664
    }
  }
2665

2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696
  static Maybe<bool> CollectValuesOrEntriesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
      PropertyFilter filter) {
    int count = 0;
    if (get_entries) {
      // Collecting entries needs to allocate, so this code must be handlified.
      Handle<FixedArray> elements(FixedArray::cast(object->elements()),
                                  isolate);
      uint32_t length = elements->length();
      for (uint32_t index = 0; index < length; ++index) {
        if (!Subclass::HasEntryImpl(isolate, *elements, index)) continue;
        Handle<Object> value = Subclass::GetImpl(isolate, *elements, index);
        value = MakeEntryPair(isolate, index, value);
        values_or_entries->set(count++, *value);
      }
    } else {
      // No allocations here, so we can avoid handlification overhead.
      DisallowHeapAllocation no_gc;
      FixedArray* elements = FixedArray::cast(object->elements());
      uint32_t length = elements->length();
      for (uint32_t index = 0; index < length; ++index) {
        if (!Subclass::HasEntryImpl(isolate, elements, index)) continue;
        Object* value = GetRaw(elements, index);
        values_or_entries->set(count++, value);
      }
    }
    *nof_items = count;
    return Just(true);
  }

2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710
  static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
                                         Handle<JSObject> receiver,
                                         Handle<Object> search_value,
                                         uint32_t start_from, uint32_t length) {
    DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
    DisallowHeapAllocation no_gc;
    FixedArrayBase* elements_base = receiver->elements();
    Object* value = *search_value;

    if (start_from >= length) return Just<int64_t>(-1);

    length = std::min(static_cast<uint32_t>(elements_base->length()), length);

    // Only FAST_{,HOLEY_}ELEMENTS can store non-numbers.
2711
    if (!value->IsNumber() && !IsObjectElementsKind(Subclass::kind())) {
2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722
      return Just<int64_t>(-1);
    }
    // NaN can never be found by strict equality.
    if (value->IsNaN()) return Just<int64_t>(-1);

    FixedArray* elements = FixedArray::cast(receiver->elements());
    for (uint32_t k = start_from; k < length; ++k) {
      if (value->StrictEquals(elements->get(k))) return Just<int64_t>(k);
    }
    return Just<int64_t>(-1);
  }
2723
};
2724

2725 2726
class FastPackedSmiElementsAccessor
    : public FastSmiOrObjectElementsAccessor<
2727 2728
          FastPackedSmiElementsAccessor,
          ElementsKindTraits<PACKED_SMI_ELEMENTS>> {
2729 2730 2731
 public:
  explicit FastPackedSmiElementsAccessor(const char* name)
      : FastSmiOrObjectElementsAccessor<
2732 2733
            FastPackedSmiElementsAccessor,
            ElementsKindTraits<PACKED_SMI_ELEMENTS>>(name) {}
2734 2735 2736 2737
};

class FastHoleySmiElementsAccessor
    : public FastSmiOrObjectElementsAccessor<
2738 2739
          FastHoleySmiElementsAccessor,
          ElementsKindTraits<HOLEY_SMI_ELEMENTS>> {
2740 2741
 public:
  explicit FastHoleySmiElementsAccessor(const char* name)
2742 2743 2744
      : FastSmiOrObjectElementsAccessor<FastHoleySmiElementsAccessor,
                                        ElementsKindTraits<HOLEY_SMI_ELEMENTS>>(
            name) {}
2745 2746
};

2747 2748
class FastPackedObjectElementsAccessor
    : public FastSmiOrObjectElementsAccessor<
2749 2750
          FastPackedObjectElementsAccessor,
          ElementsKindTraits<PACKED_ELEMENTS>> {
2751 2752
 public:
  explicit FastPackedObjectElementsAccessor(const char* name)
2753 2754 2755
      : FastSmiOrObjectElementsAccessor<FastPackedObjectElementsAccessor,
                                        ElementsKindTraits<PACKED_ELEMENTS>>(
            name) {}
2756 2757 2758 2759
};

class FastHoleyObjectElementsAccessor
    : public FastSmiOrObjectElementsAccessor<
2760
          FastHoleyObjectElementsAccessor, ElementsKindTraits<HOLEY_ELEMENTS>> {
2761 2762
 public:
  explicit FastHoleyObjectElementsAccessor(const char* name)
2763 2764 2765
      : FastSmiOrObjectElementsAccessor<FastHoleyObjectElementsAccessor,
                                        ElementsKindTraits<HOLEY_ELEMENTS>>(
            name) {}
2766 2767
};

2768
template <typename Subclass, typename KindTraits>
2769
class FastDoubleElementsAccessor
2770
    : public FastElementsAccessor<Subclass, KindTraits> {
2771 2772
 public:
  explicit FastDoubleElementsAccessor(const char* name)
2773
      : FastElementsAccessor<Subclass, KindTraits>(name) {}
2774

2775 2776
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* backing_store,
                                uint32_t entry) {
2777 2778 2779 2780 2781 2782 2783 2784 2785
    return FixedDoubleArray::get(FixedDoubleArray::cast(backing_store), entry,
                                 isolate);
  }

  static inline void SetImpl(Handle<JSObject> holder, uint32_t entry,
                             Object* value) {
    SetImpl(holder->elements(), entry, value);
  }

2786 2787 2788 2789 2790 2791 2792 2793 2794 2795
  static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry,
                             Object* value) {
    FixedDoubleArray::cast(backing_store)->set(entry, value->Number());
  }

  static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry,
                             Object* value, WriteBarrierMode mode) {
    FixedDoubleArray::cast(backing_store)->set(entry, value->Number());
  }

2796 2797 2798 2799
  static void CopyElementsImpl(Isolate* isolate, FixedArrayBase* from,
                               uint32_t from_start, FixedArrayBase* to,
                               ElementsKind from_kind, uint32_t to_start,
                               int packed_size, int copy_size) {
2800
    DisallowHeapAllocation no_allocation;
2801
    switch (from_kind) {
2802
      case PACKED_SMI_ELEMENTS:
2803
        CopyPackedSmiToDoubleElements(from, from_start, to, to_start,
2804
                                      packed_size, copy_size);
2805
        break;
2806
      case HOLEY_SMI_ELEMENTS:
2807
        CopySmiToDoubleElements(from, from_start, to, to_start, copy_size);
2808
        break;
2809 2810
      case PACKED_DOUBLE_ELEMENTS:
      case HOLEY_DOUBLE_ELEMENTS:
2811
        CopyDoubleToDoubleElements(from, from_start, to, to_start, copy_size);
2812
        break;
2813 2814
      case PACKED_ELEMENTS:
      case HOLEY_ELEMENTS:
2815
        CopyObjectToDoubleElements(from, from_start, to, to_start, copy_size);
2816 2817
        break;
      case DICTIONARY_ELEMENTS:
2818
        CopyDictionaryToDoubleElements(isolate, from, from_start, to, to_start,
2819
                                       copy_size);
2820
        break;
2821 2822
      case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
      case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
2823 2824 2825 2826
      case FAST_STRING_WRAPPER_ELEMENTS:
      case SLOW_STRING_WRAPPER_ELEMENTS:
      case NO_ELEMENTS:
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
2827 2828
      TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
2829 2830 2831 2832
      // This function is currently only used for JSArrays with non-zero
      // length.
      UNREACHABLE();
      break;
2833 2834
    }
  }
2835

2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855
  static Maybe<bool> CollectValuesOrEntriesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
      PropertyFilter filter) {
    Handle<FixedDoubleArray> elements(
        FixedDoubleArray::cast(object->elements()), isolate);
    int count = 0;
    uint32_t length = elements->length();
    for (uint32_t index = 0; index < length; ++index) {
      if (!Subclass::HasEntryImpl(isolate, *elements, index)) continue;
      Handle<Object> value = Subclass::GetImpl(isolate, *elements, index);
      if (get_entries) {
        value = MakeEntryPair(isolate, index, value);
      }
      values_or_entries->set(count++, *value);
    }
    *nof_items = count;
    return Just(true);
  }

2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866
  static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
                                         Handle<JSObject> receiver,
                                         Handle<Object> search_value,
                                         uint32_t start_from, uint32_t length) {
    DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
    DisallowHeapAllocation no_gc;
    FixedArrayBase* elements_base = receiver->elements();
    Object* value = *search_value;

    length = std::min(static_cast<uint32_t>(elements_base->length()), length);

2867 2868
    if (start_from >= length) return Just<int64_t>(-1);

2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887
    if (!value->IsNumber()) {
      return Just<int64_t>(-1);
    }
    if (value->IsNaN()) {
      return Just<int64_t>(-1);
    }
    double numeric_search_value = value->Number();
    FixedDoubleArray* elements = FixedDoubleArray::cast(receiver->elements());

    for (uint32_t k = start_from; k < length; ++k) {
      if (elements->is_the_hole(k)) {
        continue;
      }
      if (elements->get_scalar(k) == numeric_search_value) {
        return Just<int64_t>(k);
      }
    }
    return Just<int64_t>(-1);
  }
2888
};
2889

2890 2891
class FastPackedDoubleElementsAccessor
    : public FastDoubleElementsAccessor<
2892 2893
          FastPackedDoubleElementsAccessor,
          ElementsKindTraits<PACKED_DOUBLE_ELEMENTS>> {
2894 2895
 public:
  explicit FastPackedDoubleElementsAccessor(const char* name)
2896 2897 2898
      : FastDoubleElementsAccessor<FastPackedDoubleElementsAccessor,
                                   ElementsKindTraits<PACKED_DOUBLE_ELEMENTS>>(
            name) {}
2899 2900 2901 2902
};

class FastHoleyDoubleElementsAccessor
    : public FastDoubleElementsAccessor<
2903 2904
          FastHoleyDoubleElementsAccessor,
          ElementsKindTraits<HOLEY_DOUBLE_ELEMENTS>> {
2905 2906
 public:
  explicit FastHoleyDoubleElementsAccessor(const char* name)
2907 2908 2909
      : FastDoubleElementsAccessor<FastHoleyDoubleElementsAccessor,
                                   ElementsKindTraits<HOLEY_DOUBLE_ELEMENTS>>(
            name) {}
2910 2911 2912 2913
};


// Super class for all external element arrays.
2914
template <ElementsKind Kind, typename ctype>
2915
class TypedElementsAccessor
2916 2917
    : public ElementsAccessorBase<TypedElementsAccessor<Kind, ctype>,
                                  ElementsKindTraits<Kind>> {
2918
 public:
2919
  explicit TypedElementsAccessor(const char* name)
2920
      : ElementsAccessorBase<AccessorClass,
2921
                             ElementsKindTraits<Kind> >(name) {}
2922

2923
  typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore;
2924
  typedef TypedElementsAccessor<Kind, ctype> AccessorClass;
2925

2926 2927 2928 2929 2930
  static inline void SetImpl(Handle<JSObject> holder, uint32_t entry,
                             Object* value) {
    SetImpl(holder->elements(), entry, value);
  }

2931 2932 2933 2934 2935 2936 2937 2938 2939 2940
  static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry,
                             Object* value) {
    BackingStore::cast(backing_store)->SetValue(entry, value);
  }

  static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry,
                             Object* value, WriteBarrierMode mode) {
    BackingStore::cast(backing_store)->SetValue(entry, value);
  }

2941 2942
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* backing_store,
                                uint32_t entry) {
2943 2944 2945 2946
    return BackingStore::get(BackingStore::cast(backing_store), entry);
  }

  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
2947
    return PropertyDetails(kData, DONT_DELETE, PropertyCellType::kNoCell);
2948
  }
2949

2950
  static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
2951
                                        uint32_t entry) {
2952
    return PropertyDetails(kData, DONT_DELETE, PropertyCellType::kNoCell);
2953 2954
  }

2955 2956
  static bool HasElementImpl(Isolate* isolate, JSObject* holder, uint32_t index,
                             FixedArrayBase* backing_store,
2957
                             PropertyFilter filter) {
2958
    return index < AccessorClass::GetCapacityImpl(holder, backing_store);
2959 2960
  }

2961 2962 2963 2964 2965
  static bool HasAccessorsImpl(JSObject* holder,
                               FixedArrayBase* backing_store) {
    return false;
  }

2966 2967
  static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
                            uint32_t length,
2968
                            Handle<FixedArrayBase> backing_store) {
2969 2970 2971 2972
    // External arrays do not support changing their length.
    UNREACHABLE();
  }

2973
  static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
2974
    UNREACHABLE();
2975
  }
2976

2977 2978 2979 2980 2981
  static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store,
                                       uint32_t entry) {
    return entry;
  }

2982
  static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder,
2983
                                       FixedArrayBase* backing_store,
2984
                                       uint32_t index, PropertyFilter filter) {
2985 2986
    return index < AccessorClass::GetCapacityImpl(holder, backing_store)
               ? index
2987
               : kMaxUInt32;
2988
  }
2989

2990 2991 2992 2993 2994
  static bool WasNeutered(JSObject* holder) {
    JSArrayBufferView* view = JSArrayBufferView::cast(holder);
    return view->WasNeutered();
  }

2995 2996
  static uint32_t GetCapacityImpl(JSObject* holder,
                                  FixedArrayBase* backing_store) {
2997
    if (WasNeutered(holder)) return 0;
2998 2999
    return backing_store->length();
  }
3000

3001 3002 3003 3004 3005
  static uint32_t NumberOfElementsImpl(JSObject* receiver,
                                       FixedArrayBase* backing_store) {
    return AccessorClass::GetCapacityImpl(receiver, backing_store);
  }

3006 3007 3008
  static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
                                              KeyAccumulator* accumulator,
                                              AddKeyConversion convert) {
3009
    Isolate* isolate = receiver->GetIsolate();
3010
    Handle<FixedArrayBase> elements(receiver->elements(), isolate);
3011 3012
    uint32_t length = AccessorClass::GetCapacityImpl(*receiver, *elements);
    for (uint32_t i = 0; i < length; i++) {
3013
      Handle<Object> value = AccessorClass::GetImpl(isolate, *elements, i);
3014 3015 3016
      accumulator->AddKey(value, convert);
    }
  }
3017 3018 3019 3020 3021 3022 3023

  static Maybe<bool> CollectValuesOrEntriesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
      PropertyFilter filter) {
    int count = 0;
    if ((filter & ONLY_CONFIGURABLE) == 0) {
3024
      Handle<FixedArrayBase> elements(object->elements(), isolate);
3025 3026
      uint32_t length = AccessorClass::GetCapacityImpl(*object, *elements);
      for (uint32_t index = 0; index < length; ++index) {
3027 3028
        Handle<Object> value =
            AccessorClass::GetImpl(isolate, *elements, index);
3029 3030 3031 3032 3033 3034 3035 3036 3037
        if (get_entries) {
          value = MakeEntryPair(isolate, index, value);
        }
        values_or_entries->set(count++, *value);
      }
    }
    *nof_items = count;
    return Just(true);
  }
3038

3039 3040 3041 3042 3043
  static Object* FillImpl(Isolate* isolate, Handle<JSObject> receiver,
                          Handle<Object> obj_value, uint32_t start,
                          uint32_t end) {
    Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(receiver);
    DCHECK(!array->WasNeutered());
3044
    DCHECK(obj_value->IsNumeric());
3045

3046
    ctype value = BackingStore::FromHandle(obj_value);
3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059

    // Ensure indexes are within array bounds
    DCHECK_LE(0, start);
    DCHECK_LE(start, end);
    DCHECK_LE(end, array->length_value());

    DisallowHeapAllocation no_gc;
    BackingStore* elements = BackingStore::cast(receiver->elements());
    ctype* data = static_cast<ctype*>(elements->DataPtr());
    std::fill(data + start, data + end, value);
    return *array;
  }

3060 3061 3062 3063 3064
  static Maybe<bool> IncludesValueImpl(Isolate* isolate,
                                       Handle<JSObject> receiver,
                                       Handle<Object> value,
                                       uint32_t start_from, uint32_t length) {
    DisallowHeapAllocation no_gc;
3065

3066 3067 3068 3069 3070 3071
    // TODO(caitp): return Just(false) here when implementing strict throwing on
    // neutered views.
    if (WasNeutered(*receiver)) {
      return Just(value->IsUndefined(isolate) && length > start_from);
    }

3072 3073 3074 3075 3076
    BackingStore* elements = BackingStore::cast(receiver->elements());
    if (value->IsUndefined(isolate) &&
        length > static_cast<uint32_t>(elements->length())) {
      return Just(true);
    }
3077
    ctype typed_search_value;
3078 3079 3080 3081 3082 3083
    // Prototype has no elements, and not searching for the hole --- limit
    // search to backing store length.
    if (static_cast<uint32_t>(elements->length()) < length) {
      length = elements->length();
    }

3084 3085 3086 3087 3088
    if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
      if (!value->IsBigInt()) return Just(false);
      bool lossless;
      typed_search_value = BackingStore::FromHandle(value, &lossless);
      if (!lossless) return Just(false);
3089
    } else {
3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111
      if (!value->IsNumber()) return Just(false);
      double search_value = value->Number();
      if (!std::isfinite(search_value)) {
        // Integral types cannot represent +Inf or NaN.
        if (Kind < FLOAT32_ELEMENTS || Kind > FLOAT64_ELEMENTS) {
          return Just(false);
        }
        if (std::isnan(search_value)) {
          for (uint32_t k = start_from; k < length; ++k) {
            double element_k = elements->get_scalar(k);
            if (std::isnan(element_k)) return Just(true);
          }
          return Just(false);
        }
      } else if (search_value < std::numeric_limits<ctype>::lowest() ||
                 search_value > std::numeric_limits<ctype>::max()) {
        // Return false if value can't be represented in this space.
        return Just(false);
      }
      typed_search_value = static_cast<ctype>(search_value);
      if (static_cast<double>(typed_search_value) != search_value) {
        return Just(false);  // Loss of precision.
3112 3113
      }
    }
3114 3115 3116 3117 3118 3119

    for (uint32_t k = start_from; k < length; ++k) {
      ctype element_k = elements->get_scalar(k);
      if (element_k == typed_search_value) return Just(true);
    }
    return Just(false);
3120
  }
3121 3122 3123 3124 3125 3126 3127

  static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
                                         Handle<JSObject> receiver,
                                         Handle<Object> value,
                                         uint32_t start_from, uint32_t length) {
    DisallowHeapAllocation no_gc;

3128 3129
    if (WasNeutered(*receiver)) return Just<int64_t>(-1);

3130
    BackingStore* elements = BackingStore::cast(receiver->elements());
3131
    ctype typed_search_value;
3132

3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151
    if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
      if (!value->IsBigInt()) return Just<int64_t>(-1);
      bool lossless;
      typed_search_value = BackingStore::FromHandle(value, &lossless);
      if (!lossless) return Just<int64_t>(-1);
    } else {
      if (!value->IsNumber()) return Just<int64_t>(-1);
      double search_value = value->Number();
      if (!std::isfinite(search_value)) {
        // Integral types cannot represent +Inf or NaN.
        if (Kind < FLOAT32_ELEMENTS || Kind > FLOAT64_ELEMENTS) {
          return Just<int64_t>(-1);
        }
        if (std::isnan(search_value)) {
          return Just<int64_t>(-1);
        }
      } else if (search_value < std::numeric_limits<ctype>::lowest() ||
                 search_value > std::numeric_limits<ctype>::max()) {
        // Return false if value can't be represented in this ElementsKind.
3152 3153
        return Just<int64_t>(-1);
      }
3154 3155 3156 3157
      typed_search_value = static_cast<ctype>(search_value);
      if (static_cast<double>(typed_search_value) != search_value) {
        return Just<int64_t>(-1);  // Loss of precision.
      }
3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171
    }

    // Prototype has no elements, and not searching for the hole --- limit
    // search to backing store length.
    if (static_cast<uint32_t>(elements->length()) < length) {
      length = elements->length();
    }

    for (uint32_t k = start_from; k < length; ++k) {
      ctype element_k = elements->get_scalar(k);
      if (element_k == typed_search_value) return Just<int64_t>(k);
    }
    return Just<int64_t>(-1);
  }
3172 3173 3174 3175 3176 3177 3178 3179 3180

  static Maybe<int64_t> LastIndexOfValueImpl(Isolate* isolate,
                                             Handle<JSObject> receiver,
                                             Handle<Object> value,
                                             uint32_t start_from) {
    DisallowHeapAllocation no_gc;
    DCHECK(!WasNeutered(*receiver));

    BackingStore* elements = BackingStore::cast(receiver->elements());
3181
    ctype typed_search_value;
3182

3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201
    if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
      if (!value->IsBigInt()) return Just<int64_t>(-1);
      bool lossless;
      typed_search_value = BackingStore::FromHandle(value, &lossless);
      if (!lossless) return Just<int64_t>(-1);
    } else {
      if (!value->IsNumber()) return Just<int64_t>(-1);
      double search_value = value->Number();
      if (!std::isfinite(search_value)) {
        if (std::is_integral<ctype>::value) {
          // Integral types cannot represent +Inf or NaN.
          return Just<int64_t>(-1);
        } else if (std::isnan(search_value)) {
          // Strict Equality Comparison of NaN is always false.
          return Just<int64_t>(-1);
        }
      } else if (search_value < std::numeric_limits<ctype>::lowest() ||
                 search_value > std::numeric_limits<ctype>::max()) {
        // Return -1 if value can't be represented in this ElementsKind.
3202 3203
        return Just<int64_t>(-1);
      }
3204 3205 3206 3207
      typed_search_value = static_cast<ctype>(search_value);
      if (static_cast<double>(typed_search_value) != search_value) {
        return Just<int64_t>(-1);  // Loss of precision.
      }
3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218
    }

    DCHECK_LT(start_from, elements->length());

    uint32_t k = start_from;
    do {
      ctype element_k = elements->get_scalar(k);
      if (element_k == typed_search_value) return Just<int64_t>(k);
    } while (k-- != 0);
    return Just<int64_t>(-1);
  }
3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231

  static void ReverseImpl(JSObject* receiver) {
    DisallowHeapAllocation no_gc;
    DCHECK(!WasNeutered(receiver));

    BackingStore* elements = BackingStore::cast(receiver->elements());

    uint32_t len = elements->length();
    if (len == 0) return;

    ctype* data = static_cast<ctype*>(elements->DataPtr());
    std::reverse(data, data + len);
  }
3232

3233 3234 3235 3236 3237 3238
  static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
                                                        Handle<JSObject> object,
                                                        uint32_t length) {
    DCHECK(!WasNeutered(*object));
    DCHECK(object->IsJSTypedArray());
    Handle<FixedArray> result = isolate->factory()->NewFixedArray(length);
3239 3240
    Handle<BackingStore> elements(BackingStore::cast(object->elements()),
                                  isolate);
3241 3242 3243 3244 3245 3246 3247
    for (uint32_t i = 0; i < length; i++) {
      Handle<Object> value = AccessorClass::GetImpl(isolate, *elements, i);
      result->set(i, *value);
    }
    return result;
  }

3248 3249 3250 3251 3252 3253 3254
  static void CopyTypedArrayElementsSliceImpl(JSTypedArray* source,
                                              JSTypedArray* destination,
                                              size_t start, size_t end) {
    DisallowHeapAllocation no_gc;
    DCHECK_EQ(destination->GetElementsKind(), AccessorClass::kind());
    DCHECK(!source->WasNeutered());
    DCHECK(!destination->WasNeutered());
3255
    DCHECK_LE(start, end);
3256
    DCHECK_LE(end, source->length_value());
3257

3258 3259
    size_t count = end - start;
    DCHECK_LE(count, destination->length_value());
3260

3261 3262 3263
    FixedTypedArrayBase* src_elements =
        FixedTypedArrayBase::cast(source->elements());
    BackingStore* dest_elements = BackingStore::cast(destination->elements());
3264

3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277
    size_t element_size = source->element_size();
    uint8_t* source_data =
        static_cast<uint8_t*>(src_elements->DataPtr()) + start * element_size;

    // Fast path for the same type result array
    if (source->type() == destination->type()) {
      uint8_t* dest_data = static_cast<uint8_t*>(dest_elements->DataPtr());

      // The spec defines the copy-step iteratively, which means that we
      // cannot use memcpy if the buffer is shared.
      uint8_t* end_ptr = source_data + count * element_size;
      while (source_data < end_ptr) {
        *dest_data++ = *source_data++;
3278
      }
3279
      return;
3280 3281
    }

3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292
    switch (source->GetElementsKind()) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size)                     \
  case TYPE##_ELEMENTS:                                                     \
    CopyBetweenBackingStores<Type##ArrayTraits>(source_data, dest_elements, \
                                                count, 0);                  \
    break;
      TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
      default:
        UNREACHABLE();
        break;
3293 3294
    }
  }
3295 3296 3297 3298 3299 3300 3301 3302

  static bool HasSimpleRepresentation(InstanceType type) {
    return !(type == FIXED_FLOAT32_ARRAY_TYPE ||
             type == FIXED_FLOAT64_ARRAY_TYPE ||
             type == FIXED_UINT8_CLAMPED_ARRAY_TYPE);
  }

  template <typename SourceTraits>
3303
  static void CopyBetweenBackingStores(void* source_data_ptr,
3304 3305
                                       BackingStore* dest, size_t length,
                                       uint32_t offset) {
3306
    DisallowHeapAllocation no_gc;
3307
    for (uint32_t i = 0; i < length; i++) {
3308 3309 3310 3311 3312
      // We use scalar accessors to avoid boxing/unboxing, so there are no
      // allocations.
      typename SourceTraits::ElementType elem =
          FixedTypedArray<SourceTraits>::get_scalar_from_data_ptr(
              source_data_ptr, i);
3313
      dest->set(offset + i, dest->from(elem));
3314 3315 3316
    }
  }

3317 3318 3319
  static void CopyElementsFromTypedArray(JSTypedArray* source,
                                         JSTypedArray* destination,
                                         size_t length, uint32_t offset) {
3320
    // The source is a typed array, so we know we don't need to do ToNumber
3321
    // side-effects, as the source elements will always be a number.
3322 3323
    DisallowHeapAllocation no_gc;

3324 3325 3326 3327
    FixedTypedArrayBase* source_elements =
        FixedTypedArrayBase::cast(source->elements());
    BackingStore* destination_elements =
        BackingStore::cast(destination->elements());
3328

3329
    DCHECK_LE(offset, destination->length_value());
3330
    DCHECK_LE(length, destination->length_value() - offset);
3331
    DCHECK(source->length()->IsSmi());
3332
    DCHECK_LE(length, source->length_value());
3333 3334 3335 3336 3337 3338

    InstanceType source_type = source_elements->map()->instance_type();
    InstanceType destination_type =
        destination_elements->map()->instance_type();

    bool same_type = source_type == destination_type;
3339
    bool same_size = source->element_size() == destination->element_size();
3340 3341 3342
    bool both_are_simple = HasSimpleRepresentation(source_type) &&
                           HasSimpleRepresentation(destination_type);

3343 3344
    uint8_t* source_data = static_cast<uint8_t*>(source_elements->DataPtr());
    uint8_t* dest_data = static_cast<uint8_t*>(destination_elements->DataPtr());
3345 3346
    size_t source_byte_length = NumberToSize(source->byte_length());
    size_t dest_byte_length = NumberToSize(destination->byte_length());
3347

3348 3349 3350 3351 3352
    // We can simply copy the backing store if the types are the same, or if
    // we are converting e.g. Uint8 <-> Int8, as the binary representation
    // will be the same. This is not the case for floats or clamped Uint8,
    // which have special conversion operations.
    if (same_type || (same_size && both_are_simple)) {
3353
      size_t element_size = source->element_size();
3354 3355
      std::memmove(dest_data + offset * element_size, source_data,
                   length * element_size);
3356
    } else {
3357
      std::unique_ptr<uint8_t[]> cloned_source_elements;
3358 3359 3360 3361

      // If the typedarrays are overlapped, clone the source.
      if (dest_data + dest_byte_length > source_data &&
          source_data + source_byte_length > dest_data) {
3362 3363 3364 3365
        cloned_source_elements.reset(new uint8_t[source_byte_length]);
        std::memcpy(cloned_source_elements.get(), source_data,
                    source_byte_length);
        source_data = cloned_source_elements.get();
3366 3367
      }

3368
      switch (source->GetElementsKind()) {
3369 3370 3371 3372
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size)     \
  case TYPE##_ELEMENTS:                                     \
    CopyBetweenBackingStores<Type##ArrayTraits>(            \
        source_data, destination_elements, length, offset); \
3373 3374 3375 3376 3377 3378 3379 3380 3381 3382
    break;
        TYPED_ARRAYS(TYPED_ARRAY_CASE)
        default:
          UNREACHABLE();
          break;
      }
#undef TYPED_ARRAY_CASE
    }
  }

3383 3384 3385 3386 3387
  static bool HoleyPrototypeLookupRequired(Isolate* isolate, Context* context,
                                           JSArray* source) {
    DisallowHeapAllocation no_gc;
    DisallowJavascriptExecution no_js(isolate);

3388
#ifdef V8_ENABLE_FORCE_SLOW_PATH
3389 3390 3391
    if (isolate->force_slow_path()) return true;
#endif

3392
    Object* source_proto = source->map()->prototype();
3393

3394 3395 3396 3397
    // Null prototypes are OK - we don't need to do prototype chain lookups on
    // them.
    if (source_proto->IsNull(isolate)) return false;
    if (source_proto->IsJSProxy()) return true;
3398 3399
    if (!context->native_context()->is_initial_array_prototype(
            JSObject::cast(source_proto))) {
3400 3401
      return true;
    }
3402 3403

    return !isolate->IsNoElementsProtectorIntact(context);
3404 3405
  }

3406 3407 3408
  static bool TryCopyElementsFastNumber(Context* context, JSArray* source,
                                        JSTypedArray* destination,
                                        size_t length, uint32_t offset) {
3409
    if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) return false;
3410
    Isolate* isolate = source->GetIsolate();
3411
    DisallowHeapAllocation no_gc;
3412 3413
    DisallowJavascriptExecution no_js(isolate);

3414 3415 3416 3417 3418 3419 3420 3421 3422 3423
    size_t current_length;
    DCHECK(source->length()->IsNumber() &&
           TryNumberToSize(source->length(), &current_length) &&
           length <= current_length);
    USE(current_length);

    size_t dest_length = destination->length_value();
    DCHECK(length + offset <= dest_length);
    USE(dest_length);

3424 3425 3426
    ElementsKind kind = source->GetElementsKind();
    BackingStore* dest = BackingStore::cast(destination->elements());

3427 3428 3429 3430 3431
    // When we find the hole, we normally have to look up the element on the
    // prototype chain, which is not handled here and we return false instead.
    // When the array has the original array prototype, and that prototype has
    // not been changed in a way that would affect lookups, we can just convert
    // the hole into undefined.
3432
    if (HoleyPrototypeLookupRequired(isolate, context, source)) return false;
3433 3434 3435 3436

    Object* undefined = isolate->heap()->undefined_value();

    // Fastpath for packed Smi kind.
3437
    if (kind == PACKED_SMI_ELEMENTS) {
3438 3439 3440 3441 3442
      FixedArray* source_store = FixedArray::cast(source->elements());

      for (uint32_t i = 0; i < length; i++) {
        Object* elem = source_store->get(i);
        DCHECK(elem->IsSmi());
jgruber's avatar
jgruber committed
3443
        int int_value = Smi::ToInt(elem);
3444
        dest->set(offset + i, dest->from(int_value));
3445 3446
      }
      return true;
3447
    } else if (kind == HOLEY_SMI_ELEMENTS) {
3448 3449 3450
      FixedArray* source_store = FixedArray::cast(source->elements());
      for (uint32_t i = 0; i < length; i++) {
        if (source_store->is_the_hole(isolate, i)) {
3451
          dest->SetValue(offset + i, undefined);
3452 3453 3454
        } else {
          Object* elem = source_store->get(i);
          DCHECK(elem->IsSmi());
jgruber's avatar
jgruber committed
3455
          int int_value = Smi::ToInt(elem);
3456
          dest->set(offset + i, dest->from(int_value));
3457 3458 3459
        }
      }
      return true;
3460
    } else if (kind == PACKED_DOUBLE_ELEMENTS) {
3461 3462 3463 3464 3465 3466 3467 3468 3469
      // Fastpath for packed double kind. We avoid boxing and then immediately
      // unboxing the double here by using get_scalar.
      FixedDoubleArray* source_store =
          FixedDoubleArray::cast(source->elements());

      for (uint32_t i = 0; i < length; i++) {
        // Use the from_double conversion for this specific TypedArray type,
        // rather than relying on C++ to convert elem.
        double elem = source_store->get_scalar(i);
3470
        dest->set(offset + i, dest->from(elem));
3471 3472
      }
      return true;
3473
    } else if (kind == HOLEY_DOUBLE_ELEMENTS) {
3474 3475 3476 3477
      FixedDoubleArray* source_store =
          FixedDoubleArray::cast(source->elements());
      for (uint32_t i = 0; i < length; i++) {
        if (source_store->is_the_hole(i)) {
3478
          dest->SetValue(offset + i, undefined);
3479 3480
        } else {
          double elem = source_store->get_scalar(i);
3481
          dest->set(offset + i, dest->from(elem));
3482 3483 3484
        }
      }
      return true;
3485 3486 3487 3488
    }
    return false;
  }

3489
  static Object* CopyElementsHandleSlow(Handle<Object> source,
3490
                                        Handle<JSTypedArray> destination,
3491
                                        size_t length, uint32_t offset) {
3492
    Isolate* isolate = destination->GetIsolate();
3493
    Handle<BackingStore> destination_elements(
3494
        BackingStore::cast(destination->elements()), isolate);
3495
    for (uint32_t i = 0; i < length; i++) {
3496
      LookupIterator it(isolate, source, i);
3497 3498 3499
      Handle<Object> elem;
      ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
                                         Object::GetProperty(&it));
3500 3501 3502 3503 3504 3505 3506
      if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
        ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
                                           BigInt::FromObject(isolate, elem));
      } else {
        ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
                                           Object::ToNumber(elem));
      }
3507 3508 3509 3510 3511 3512 3513 3514 3515 3516

      if (V8_UNLIKELY(destination->WasNeutered())) {
        const char* op = "set";
        const MessageTemplate::Template message =
            MessageTemplate::kDetachedOperation;
        Handle<String> operation =
            isolate->factory()->NewStringFromAsciiChecked(op);
        THROW_NEW_ERROR_RETURN_FAILURE(isolate,
                                       NewTypeError(message, operation));
      }
3517 3518
      // The spec says we store the length, then get each element, so we don't
      // need to check changes to length.
3519
      destination_elements->SetValue(offset + i, *elem);
3520
    }
3521
    return *isolate->factory()->undefined_value();
3522 3523
  }

3524 3525 3526
  // This doesn't guarantee that the destination array will be completely
  // filled. The caller must do this by passing a source with equal length, if
  // that is required.
3527
  static Object* CopyElementsHandleImpl(Handle<Object> source,
3528
                                        Handle<JSObject> destination,
3529 3530
                                        size_t length, uint32_t offset) {
    Isolate* isolate = destination->GetIsolate();
3531 3532
    Handle<JSTypedArray> destination_ta =
        Handle<JSTypedArray>::cast(destination);
3533
    DCHECK_LE(offset + length, destination_ta->length_value());
3534

3535 3536
    if (length == 0) return *isolate->factory()->undefined_value();

3537 3538 3539
    // All conversions from TypedArrays can be done without allocation.
    if (source->IsJSTypedArray()) {
      Handle<JSTypedArray> source_ta = Handle<JSTypedArray>::cast(source);
3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557
      ElementsKind source_kind = source_ta->GetElementsKind();
      bool source_is_bigint =
          source_kind == BIGINT64_ELEMENTS || source_kind == BIGUINT64_ELEMENTS;
      bool target_is_bigint =
          Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS;
      if (target_is_bigint) {
        if (V8_UNLIKELY(!source_is_bigint)) {
          Handle<Object> first =
              JSReceiver::GetElement(isolate, source_ta, 0).ToHandleChecked();
          THROW_NEW_ERROR_RETURN_FAILURE(
              isolate, NewTypeError(MessageTemplate::kBigIntFromObject, first));
        }
      } else {
        if (V8_UNLIKELY(source_is_bigint)) {
          THROW_NEW_ERROR_RETURN_FAILURE(
              isolate, NewTypeError(MessageTemplate::kBigIntToNumber));
        }
      }
3558 3559 3560
      // If we have to copy more elements than we have in the source, we need to
      // do special handling and conversion; that happens in the slow case.
      if (length + offset <= source_ta->length_value()) {
3561
        DCHECK(length == 0 || !source_ta->WasNeutered());
3562 3563 3564
        CopyElementsFromTypedArray(*source_ta, *destination_ta, length, offset);
        return *isolate->factory()->undefined_value();
      }
3565 3566 3567 3568
    }

    // Fast cases for packed numbers kinds where we don't need to allocate.
    if (source->IsJSArray()) {
3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579
      Handle<JSArray> source_js_array = Handle<JSArray>::cast(source);
      size_t current_length;
      if (source_js_array->length()->IsNumber() &&
          TryNumberToSize(source_js_array->length(), &current_length)) {
        if (length <= current_length) {
          Handle<JSArray> source_array = Handle<JSArray>::cast(source);
          if (TryCopyElementsFastNumber(isolate->context(), *source_array,
                                        *destination_ta, length, offset)) {
            return *isolate->factory()->undefined_value();
          }
        }
3580 3581 3582 3583
      }
    }
    // Final generic case that handles prototype chain lookups, getters, proxies
    // and observable side effects via valueOf, etc.
3584
    return CopyElementsHandleSlow(source, destination_ta, length, offset);
3585
  }
3586
};
3587

3588 3589
#define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype, size) \
  typedef TypedElementsAccessor<TYPE##_ELEMENTS, ctype>        \
3590
      Fixed##Type##ElementsAccessor;
3591

3592 3593
TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR)
#undef FIXED_ELEMENTS_ACCESSOR
3594

3595
template <typename Subclass, typename ArgumentsAccessor, typename KindTraits>
3596
class SloppyArgumentsElementsAccessor
3597
    : public ElementsAccessorBase<Subclass, KindTraits> {
3598 3599
 public:
  explicit SloppyArgumentsElementsAccessor(const char* name)
3600
      : ElementsAccessorBase<Subclass, KindTraits>(name) {
3601 3602
    USE(KindTraits::Kind);
  }
3603

3604 3605 3606 3607 3608 3609
  static void ConvertArgumentsStoreResult(
      Isolate* isolate, Handle<SloppyArgumentsElements> elements,
      Handle<Object> result) {
    UNREACHABLE();
  }

3610 3611
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* parameters,
                                uint32_t entry) {
3612 3613 3614
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(parameters), isolate);
    uint32_t length = elements->parameter_map_length();
3615
    if (entry < length) {
3616
      // Read context mapped entry.
3617
      DisallowHeapAllocation no_gc;
3618 3619 3620
      Object* probe = elements->get_mapped_entry(entry);
      DCHECK(!probe->IsTheHole(isolate));
      Context* context = elements->context();
jgruber's avatar
jgruber committed
3621
      int context_entry = Smi::ToInt(probe);
3622
      DCHECK(!context->get(context_entry)->IsTheHole(isolate));
3623
      return handle(context->get(context_entry), isolate);
3624
    } else {
3625
      // Entry is not context mapped, defer to the arguments.
3626
      Handle<Object> result = ArgumentsAccessor::GetImpl(
3627 3628
          isolate, elements->arguments(), entry - length);
      return Subclass::ConvertArgumentsStoreResult(isolate, elements, result);
3629 3630
    }
  }
3631

3632 3633 3634 3635 3636
  static void TransitionElementsKindImpl(Handle<JSObject> object,
                                         Handle<Map> map) {
    UNREACHABLE();
  }

3637 3638
  static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
                                         uint32_t capacity) {
3639
    UNREACHABLE();
3640 3641
  }

3642 3643 3644 3645 3646
  static inline void SetImpl(Handle<JSObject> holder, uint32_t entry,
                             Object* value) {
    SetImpl(holder->elements(), entry, value);
  }

3647 3648
  static inline void SetImpl(FixedArrayBase* store, uint32_t entry,
                             Object* value) {
3649 3650
    SloppyArgumentsElements* elements = SloppyArgumentsElements::cast(store);
    uint32_t length = elements->parameter_map_length();
3651
    if (entry < length) {
3652 3653 3654
      // Store context mapped entry.
      DisallowHeapAllocation no_gc;
      Object* probe = elements->get_mapped_entry(entry);
3655
      DCHECK(!probe->IsTheHole());
3656
      Context* context = elements->context();
jgruber's avatar
jgruber committed
3657
      int context_entry = Smi::ToInt(probe);
3658
      DCHECK(!context->get(context_entry)->IsTheHole());
3659
      context->set(context_entry, value);
3660
    } else {
3661 3662
      //  Entry is not context mapped defer to arguments.
      FixedArray* arguments = elements->arguments();
3663 3664 3665
      Object* current = ArgumentsAccessor::GetRaw(arguments, entry - length);
      if (current->IsAliasedArgumentsEntry()) {
        AliasedArgumentsEntry* alias = AliasedArgumentsEntry::cast(current);
3666
        Context* context = elements->context();
3667
        int context_entry = alias->aliased_context_slot();
3668
        DCHECK(!context->get(context_entry)->IsTheHole());
3669 3670 3671 3672
        context->set(context_entry, value);
      } else {
        ArgumentsAccessor::SetImpl(arguments, entry - length, value);
      }
3673 3674 3675
    }
  }

3676 3677
  static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
                            uint32_t length,
3678 3679 3680
                            Handle<FixedArrayBase> parameter_map) {
    // Sloppy arguments objects are not arrays.
    UNREACHABLE();
3681 3682
  }

3683 3684 3685 3686
  static uint32_t GetCapacityImpl(JSObject* holder, FixedArrayBase* store) {
    SloppyArgumentsElements* elements = SloppyArgumentsElements::cast(store);
    FixedArray* arguments = elements->arguments();
    return elements->parameter_map_length() +
3687
           ArgumentsAccessor::GetCapacityImpl(holder, arguments);
3688 3689
  }

3690 3691
  static uint32_t GetMaxNumberOfEntries(JSObject* holder,
                                        FixedArrayBase* backing_store) {
3692 3693 3694 3695
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(backing_store);
    FixedArrayBase* arguments = elements->arguments();
    return elements->parameter_map_length() +
3696 3697 3698
           ArgumentsAccessor::GetMaxNumberOfEntries(holder, arguments);
  }

3699 3700
  static uint32_t NumberOfElementsImpl(JSObject* receiver,
                                       FixedArrayBase* backing_store) {
3701 3702 3703 3704
    Isolate* isolate = receiver->GetIsolate();
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(backing_store);
    FixedArrayBase* arguments = elements->arguments();
3705
    uint32_t nof_elements = 0;
3706
    uint32_t length = elements->parameter_map_length();
3707
    for (uint32_t entry = 0; entry < length; entry++) {
3708
      if (HasParameterMapArg(isolate, elements, entry)) nof_elements++;
3709 3710 3711 3712 3713
    }
    return nof_elements +
           ArgumentsAccessor::NumberOfElementsImpl(receiver, arguments);
  }

3714 3715 3716
  static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
                                              KeyAccumulator* accumulator,
                                              AddKeyConversion convert) {
3717 3718 3719
    Isolate* isolate = accumulator->isolate();
    Handle<FixedArrayBase> elements(receiver->elements(), isolate);
    uint32_t length = GetCapacityImpl(*receiver, *elements);
3720
    for (uint32_t entry = 0; entry < length; entry++) {
3721
      if (!HasEntryImpl(isolate, *elements, entry)) continue;
3722
      Handle<Object> value = GetImpl(isolate, *elements, entry);
3723 3724 3725 3726
      accumulator->AddKey(value, convert);
    }
  }

3727 3728
  static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* parameters,
                           uint32_t entry) {
3729 3730 3731
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(parameters);
    uint32_t length = elements->parameter_map_length();
3732
    if (entry < length) {
3733
      return HasParameterMapArg(isolate, elements, entry);
3734
    }
3735
    FixedArrayBase* arguments = elements->arguments();
3736
    return ArgumentsAccessor::HasEntryImpl(isolate, arguments, entry - length);
3737 3738
  }

3739 3740
  static bool HasAccessorsImpl(JSObject* holder,
                               FixedArrayBase* backing_store) {
3741 3742 3743
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(backing_store);
    FixedArray* arguments = elements->arguments();
3744 3745 3746
    return ArgumentsAccessor::HasAccessorsImpl(holder, arguments);
  }

3747 3748
  static uint32_t GetIndexForEntryImpl(FixedArrayBase* parameters,
                                       uint32_t entry) {
3749 3750 3751
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(parameters);
    uint32_t length = elements->parameter_map_length();
3752
    if (entry < length) return entry;
3753
    FixedArray* arguments = elements->arguments();
3754
    return ArgumentsAccessor::GetIndexForEntryImpl(arguments, entry - length);
3755 3756
  }

3757
  static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder,
3758
                                       FixedArrayBase* parameters,
3759
                                       uint32_t index, PropertyFilter filter) {
3760 3761 3762 3763
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(parameters);
    if (HasParameterMapArg(isolate, elements, index)) return index;
    FixedArray* arguments = elements->arguments();
3764 3765
    uint32_t entry = ArgumentsAccessor::GetEntryForIndexImpl(
        isolate, holder, arguments, index, filter);
3766
    if (entry == kMaxUInt32) return kMaxUInt32;
3767 3768
    // Arguments entries could overlap with the dictionary entries, hence offset
    // them by the number of context mapped entries.
3769
    return elements->parameter_map_length() + entry;
3770 3771
  }

3772
  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
3773 3774 3775
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(holder->elements());
    uint32_t length = elements->parameter_map_length();
3776
    if (entry < length) {
3777
      return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
3778
    }
3779
    FixedArray* arguments = elements->arguments();
3780
    return ArgumentsAccessor::GetDetailsImpl(arguments, entry - length);
3781
  }
3782

3783 3784 3785 3786
  static bool HasParameterMapArg(Isolate* isolate,
                                 SloppyArgumentsElements* elements,
                                 uint32_t index) {
    uint32_t length = elements->parameter_map_length();
3787
    if (index >= length) return false;
3788
    return !elements->get_mapped_entry(index)->IsTheHole(isolate);
3789
  }
3790

3791
  static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
3792
    Handle<SloppyArgumentsElements> elements(
3793
        SloppyArgumentsElements::cast(obj->elements()), obj->GetIsolate());
3794
    uint32_t length = elements->parameter_map_length();
3795 3796 3797 3798 3799 3800 3801
    uint32_t delete_or_entry = entry;
    if (entry < length) {
      delete_or_entry = kMaxUInt32;
    }
    Subclass::SloppyDeleteImpl(obj, elements, delete_or_entry);
    // SloppyDeleteImpl allocates a new dictionary elements store. For making
    // heap verification happy we postpone clearing out the mapped entry.
3802
    if (entry < length) {
3803
      elements->set_mapped_entry(entry, obj->GetHeap()->the_hole_value());
3804 3805
    }
  }
3806

3807 3808 3809 3810 3811 3812 3813
  static void SloppyDeleteImpl(Handle<JSObject> obj,
                               Handle<SloppyArgumentsElements> elements,
                               uint32_t entry) {
    // Implemented in subclasses.
    UNREACHABLE();
  }

3814 3815
  static void CollectElementIndicesImpl(Handle<JSObject> object,
                                        Handle<FixedArrayBase> backing_store,
3816
                                        KeyAccumulator* keys) {
3817 3818 3819 3820 3821
    Isolate* isolate = keys->isolate();
    uint32_t nof_indices = 0;
    Handle<FixedArray> indices = isolate->factory()->NewFixedArray(
        GetCapacityImpl(*object, *backing_store));
    DirectCollectElementIndicesImpl(isolate, object, backing_store,
3822 3823
                                    GetKeysConversion::kKeepNumbers,
                                    ENUMERABLE_STRINGS, indices, &nof_indices);
3824
    SortIndices(isolate, indices, nof_indices);
3825 3826
    for (uint32_t i = 0; i < nof_indices; i++) {
      keys->AddKey(indices->get(i));
3827 3828 3829 3830 3831 3832 3833 3834
    }
  }

  static Handle<FixedArray> DirectCollectElementIndicesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
      PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices,
      uint32_t insertion_index = 0) {
3835 3836 3837
    Handle<SloppyArgumentsElements> elements =
        Handle<SloppyArgumentsElements>::cast(backing_store);
    uint32_t length = elements->parameter_map_length();
3838 3839

    for (uint32_t i = 0; i < length; ++i) {
3840
      if (elements->get_mapped_entry(i)->IsTheHole(isolate)) continue;
3841
      if (convert == GetKeysConversion::kConvertToString) {
3842 3843 3844 3845 3846 3847 3848 3849
        Handle<String> index_string = isolate->factory()->Uint32ToString(i);
        list->set(insertion_index, *index_string);
      } else {
        list->set(insertion_index, Smi::FromInt(i), SKIP_WRITE_BARRIER);
      }
      insertion_index++;
    }

3850
    Handle<FixedArray> store(elements->arguments(), isolate);
3851 3852 3853 3854
    return ArgumentsAccessor::DirectCollectElementIndicesImpl(
        isolate, object, store, convert, filter, list, nof_indices,
        insertion_index);
  }
3855 3856 3857 3858 3859 3860

  static Maybe<bool> IncludesValueImpl(Isolate* isolate,
                                       Handle<JSObject> object,
                                       Handle<Object> value,
                                       uint32_t start_from, uint32_t length) {
    DCHECK(JSObject::PrototypeHasNoElements(isolate, *object));
3861
    Handle<Map> original_map(object->map(), isolate);
3862 3863
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
3864 3865 3866
    bool search_for_hole = value->IsUndefined(isolate);

    for (uint32_t k = start_from; k < length; ++k) {
3867
      DCHECK_EQ(object->map(), *original_map);
3868 3869
      uint32_t entry =
          GetEntryForIndexImpl(isolate, *object, *elements, k, ALL_PROPERTIES);
3870 3871 3872 3873 3874
      if (entry == kMaxUInt32) {
        if (search_for_hole) return Just(true);
        continue;
      }

3875
      Handle<Object> element_k = Subclass::GetImpl(isolate, *elements, entry);
3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896

      if (element_k->IsAccessorPair()) {
        LookupIterator it(isolate, object, k, LookupIterator::OWN);
        DCHECK(it.IsFound());
        DCHECK_EQ(it.state(), LookupIterator::ACCESSOR);
        ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k,
                                         Object::GetPropertyWithAccessor(&it),
                                         Nothing<bool>());

        if (value->SameValueZero(*element_k)) return Just(true);

        if (object->map() != *original_map) {
          // Some mutation occurred in accessor. Abort "fast" path
          return IncludesValueSlowPath(isolate, object, value, k + 1, length);
        }
      } else if (value->SameValueZero(*element_k)) {
        return Just(true);
      }
    }
    return Just(false);
  }
3897 3898 3899 3900 3901 3902

  static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
                                         Handle<JSObject> object,
                                         Handle<Object> value,
                                         uint32_t start_from, uint32_t length) {
    DCHECK(JSObject::PrototypeHasNoElements(isolate, *object));
3903
    Handle<Map> original_map(object->map(), isolate);
3904 3905
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
3906 3907

    for (uint32_t k = start_from; k < length; ++k) {
3908
      DCHECK_EQ(object->map(), *original_map);
3909 3910
      uint32_t entry =
          GetEntryForIndexImpl(isolate, *object, *elements, k, ALL_PROPERTIES);
3911 3912 3913 3914
      if (entry == kMaxUInt32) {
        continue;
      }

3915
      Handle<Object> element_k = Subclass::GetImpl(isolate, *elements, entry);
3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938

      if (element_k->IsAccessorPair()) {
        LookupIterator it(isolate, object, k, LookupIterator::OWN);
        DCHECK(it.IsFound());
        DCHECK_EQ(it.state(), LookupIterator::ACCESSOR);
        ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k,
                                         Object::GetPropertyWithAccessor(&it),
                                         Nothing<int64_t>());

        if (value->StrictEquals(*element_k)) {
          return Just<int64_t>(k);
        }

        if (object->map() != *original_map) {
          // Some mutation occurred in accessor. Abort "fast" path.
          return IndexOfValueSlowPath(isolate, object, value, k + 1, length);
        }
      } else if (value->StrictEquals(*element_k)) {
        return Just<int64_t>(k);
      }
    }
    return Just<int64_t>(-1);
  }
3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961

  static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
                                    uint32_t end) {
    Isolate* isolate = receiver->GetIsolate();
    uint32_t result_len = end < start ? 0u : end - start;
    Handle<JSArray> result_array =
        isolate->factory()->NewJSArray(HOLEY_ELEMENTS, result_len, result_len);
    DisallowHeapAllocation no_gc;
    FixedArray* elements = FixedArray::cast(result_array->elements());
    FixedArray* parameters = FixedArray::cast(receiver->elements());
    uint32_t insertion_index = 0;
    for (uint32_t i = start; i < end; i++) {
      uint32_t entry = GetEntryForIndexImpl(isolate, *receiver, parameters, i,
                                            ALL_PROPERTIES);
      if (entry != kMaxUInt32 && HasEntryImpl(isolate, parameters, entry)) {
        elements->set(insertion_index, *GetImpl(isolate, parameters, entry));
      } else {
        elements->set_the_hole(isolate, insertion_index);
      }
      insertion_index++;
    }
    return result_array;
  }
3962 3963 3964
};


3965 3966 3967 3968 3969 3970 3971 3972 3973 3974
class SlowSloppyArgumentsElementsAccessor
    : public SloppyArgumentsElementsAccessor<
          SlowSloppyArgumentsElementsAccessor, DictionaryElementsAccessor,
          ElementsKindTraits<SLOW_SLOPPY_ARGUMENTS_ELEMENTS> > {
 public:
  explicit SlowSloppyArgumentsElementsAccessor(const char* name)
      : SloppyArgumentsElementsAccessor<
            SlowSloppyArgumentsElementsAccessor, DictionaryElementsAccessor,
            ElementsKindTraits<SLOW_SLOPPY_ARGUMENTS_ELEMENTS> >(name) {}

3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988
  static Handle<Object> ConvertArgumentsStoreResult(
      Isolate* isolate, Handle<SloppyArgumentsElements> elements,
      Handle<Object> result) {
    // Elements of the arguments object in slow mode might be slow aliases.
    if (result->IsAliasedArgumentsEntry()) {
      DisallowHeapAllocation no_gc;
      AliasedArgumentsEntry* alias = AliasedArgumentsEntry::cast(*result);
      Context* context = elements->context();
      int context_entry = alias->aliased_context_slot();
      DCHECK(!context->get(context_entry)->IsTheHole(isolate));
      return handle(context->get(context_entry), isolate);
    }
    return result;
  }
3989 3990 3991 3992 3993
  static void SloppyDeleteImpl(Handle<JSObject> obj,
                               Handle<SloppyArgumentsElements> elements,
                               uint32_t entry) {
    // No need to delete a context mapped entry from the arguments elements.
    if (entry == kMaxUInt32) return;
3994
    Isolate* isolate = obj->GetIsolate();
3995 3996
    Handle<NumberDictionary> dict(NumberDictionary::cast(elements->arguments()),
                                  isolate);
3997
    int length = elements->parameter_map_length();
3998
    dict = NumberDictionary::DeleteEntry(dict, entry - length);
3999
    elements->set_arguments(*dict);
4000
  }
4001
  static void AddImpl(Handle<JSObject> object, uint32_t index,
4002 4003
                      Handle<Object> value, PropertyAttributes attributes,
                      uint32_t new_capacity) {
4004 4005 4006 4007 4008
    Isolate* isolate = object->GetIsolate();
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
    Handle<FixedArrayBase> old_arguments(
        FixedArrayBase::cast(elements->arguments()), isolate);
4009 4010 4011
    Handle<NumberDictionary> dictionary =
        old_arguments->IsNumberDictionary()
            ? Handle<NumberDictionary>::cast(old_arguments)
4012
            : JSObject::NormalizeElements(object);
4013
    PropertyDetails details(kData, attributes, PropertyCellType::kNoCell);
4014 4015
    Handle<NumberDictionary> new_dictionary =
        NumberDictionary::Add(dictionary, index, value, details);
4016
    if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
4017
    if (*dictionary != *new_dictionary) {
4018
      elements->set_arguments(*new_dictionary);
4019 4020 4021 4022
    }
  }

  static void ReconfigureImpl(Handle<JSObject> object,
4023
                              Handle<FixedArrayBase> store, uint32_t entry,
4024 4025
                              Handle<Object> value,
                              PropertyAttributes attributes) {
4026
    Isolate* isolate = object->GetIsolate();
4027 4028 4029
    Handle<SloppyArgumentsElements> elements =
        Handle<SloppyArgumentsElements>::cast(store);
    uint32_t length = elements->parameter_map_length();
4030
    if (entry < length) {
4031
      Object* probe = elements->get_mapped_entry(entry);
4032
      DCHECK(!probe->IsTheHole(isolate));
4033
      Context* context = elements->context();
jgruber's avatar
jgruber committed
4034
      int context_entry = Smi::ToInt(probe);
4035
      DCHECK(!context->get(context_entry)->IsTheHole(isolate));
4036
      context->set(context_entry, *value);
4037 4038

      // Redefining attributes of an aliased element destroys fast aliasing.
4039
      elements->set_mapped_entry(entry, isolate->heap()->the_hole_value());
4040 4041
      // For elements that are still writable we re-establish slow aliasing.
      if ((attributes & READ_ONLY) == 0) {
4042
        value = isolate->factory()->NewAliasedArgumentsEntry(context_entry);
4043 4044
      }

4045
      PropertyDetails details(kData, attributes, PropertyCellType::kNoCell);
4046 4047 4048
      Handle<NumberDictionary> arguments(
          NumberDictionary::cast(elements->arguments()), isolate);
      arguments = NumberDictionary::Add(arguments, entry, value, details);
4049 4050 4051 4052
      // If the attributes were NONE, we would have called set rather than
      // reconfigure.
      DCHECK_NE(NONE, attributes);
      object->RequireSlowElements(*arguments);
4053
      elements->set_arguments(*arguments);
4054
    } else {
4055
      Handle<FixedArrayBase> arguments(elements->arguments(), isolate);
4056
      DictionaryElementsAccessor::ReconfigureImpl(
4057
          object, arguments, entry - length, value, attributes);
4058 4059 4060 4061 4062
    }
  }
};


4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073
class FastSloppyArgumentsElementsAccessor
    : public SloppyArgumentsElementsAccessor<
          FastSloppyArgumentsElementsAccessor, FastHoleyObjectElementsAccessor,
          ElementsKindTraits<FAST_SLOPPY_ARGUMENTS_ELEMENTS> > {
 public:
  explicit FastSloppyArgumentsElementsAccessor(const char* name)
      : SloppyArgumentsElementsAccessor<
            FastSloppyArgumentsElementsAccessor,
            FastHoleyObjectElementsAccessor,
            ElementsKindTraits<FAST_SLOPPY_ARGUMENTS_ELEMENTS> >(name) {}

4074 4075 4076 4077 4078 4079 4080
  static Handle<Object> ConvertArgumentsStoreResult(
      Isolate* isolate, Handle<SloppyArgumentsElements> paramtere_map,
      Handle<Object> result) {
    DCHECK(!result->IsAliasedArgumentsEntry());
    return result;
  }

4081
  static Handle<FixedArray> GetArguments(Isolate* isolate,
4082 4083 4084
                                         FixedArrayBase* store) {
    SloppyArgumentsElements* elements = SloppyArgumentsElements::cast(store);
    return Handle<FixedArray>(elements->arguments(), isolate);
4085 4086
  }

4087
  static Handle<NumberDictionary> NormalizeImpl(
4088
      Handle<JSObject> object, Handle<FixedArrayBase> elements) {
4089
    Handle<FixedArray> arguments =
4090
        GetArguments(object->GetIsolate(), *elements);
4091 4092 4093
    return FastHoleyObjectElementsAccessor::NormalizeImpl(object, arguments);
  }

4094
  static Handle<NumberDictionary> NormalizeArgumentsElements(
4095 4096
      Handle<JSObject> object, Handle<SloppyArgumentsElements> elements,
      uint32_t* entry) {
4097
    Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(object);
4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114
    elements->set_arguments(*dictionary);
    // kMaxUInt32 indicates that a context mapped element got deleted. In this
    // case we only normalize the elements (aka. migrate to SLOW_SLOPPY).
    if (*entry == kMaxUInt32) return dictionary;
    uint32_t length = elements->parameter_map_length();
    if (*entry >= length) {
      *entry = dictionary->FindEntry(*entry - length) + length;
    }
    return dictionary;
  }

  static void SloppyDeleteImpl(Handle<JSObject> obj,
                               Handle<SloppyArgumentsElements> elements,
                               uint32_t entry) {
    // Always normalize element on deleting an entry.
    NormalizeArgumentsElements(obj, elements, &entry);
    SlowSloppyArgumentsElementsAccessor::SloppyDeleteImpl(obj, elements, entry);
4115 4116
  }

4117
  static void AddImpl(Handle<JSObject> object, uint32_t index,
4118 4119 4120
                      Handle<Object> value, PropertyAttributes attributes,
                      uint32_t new_capacity) {
    DCHECK_EQ(NONE, attributes);
4121 4122 4123 4124
    Isolate* isolate = object->GetIsolate();
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
    Handle<FixedArray> old_arguments(elements->arguments(), isolate);
4125
    if (old_arguments->IsNumberDictionary() ||
4126
        static_cast<uint32_t>(old_arguments->length()) < new_capacity) {
4127 4128
      GrowCapacityAndConvertImpl(object, new_capacity);
    }
4129
    FixedArray* arguments = elements->arguments();
4130 4131 4132 4133 4134 4135
    // For fast holey objects, the entry equals the index. The code above made
    // sure that there's enough space to store the value. We cannot convert
    // index to entry explicitly since the slot still contains the hole, so the
    // current EntryForIndex would indicate that it is "absent" by returning
    // kMaxUInt32.
    FastHoleyObjectElementsAccessor::SetImpl(arguments, index, *value);
4136
  }
4137

4138
  static void ReconfigureImpl(Handle<JSObject> object,
4139
                              Handle<FixedArrayBase> store, uint32_t entry,
4140 4141
                              Handle<Object> value,
                              PropertyAttributes attributes) {
4142 4143
    DCHECK_EQ(object->elements(), *store);
    Handle<SloppyArgumentsElements> elements(
4144
        SloppyArgumentsElements::cast(*store), object->GetIsolate());
4145
    NormalizeArgumentsElements(object, elements, &entry);
4146
    SlowSloppyArgumentsElementsAccessor::ReconfigureImpl(object, store, entry,
4147
                                                         value, attributes);
4148
  }
4149

4150 4151 4152 4153
  static void CopyElementsImpl(Isolate* isolate, FixedArrayBase* from,
                               uint32_t from_start, FixedArrayBase* to,
                               ElementsKind from_kind, uint32_t to_start,
                               int packed_size, int copy_size) {
4154 4155
    DCHECK(!to->IsDictionary());
    if (from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS) {
4156 4157
      CopyDictionaryToObjectElements(isolate, from, from_start, to,
                                     HOLEY_ELEMENTS, to_start, copy_size);
4158 4159
    } else {
      DCHECK_EQ(FAST_SLOPPY_ARGUMENTS_ELEMENTS, from_kind);
4160
      CopyObjectToObjectElements(isolate, from, HOLEY_ELEMENTS, from_start, to,
4161
                                 HOLEY_ELEMENTS, to_start, copy_size);
4162 4163 4164 4165 4166
    }
  }

  static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
                                         uint32_t capacity) {
4167 4168 4169 4170 4171
    Isolate* isolate = object->GetIsolate();
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
    Handle<FixedArray> old_arguments(FixedArray::cast(elements->arguments()),
                                     isolate);
4172 4173 4174 4175
    ElementsKind from_kind = object->GetElementsKind();
    // This method should only be called if there's a reason to update the
    // elements.
    DCHECK(from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS ||
4176 4177 4178
           static_cast<uint32_t>(old_arguments->length()) < capacity);
    Handle<FixedArrayBase> arguments =
        ConvertElementsWithCapacity(object, old_arguments, from_kind, capacity);
4179 4180 4181
    Handle<Map> new_map = JSObject::GetElementsTransitionMap(
        object, FAST_SLOPPY_ARGUMENTS_ELEMENTS);
    JSObject::MigrateToMap(object, new_map);
4182
    elements->set_arguments(FixedArray::cast(*arguments));
4183
    JSObject::ValidateElements(*object);
4184 4185 4186
  }
};

4187
template <typename Subclass, typename BackingStoreAccessor, typename KindTraits>
4188
class StringWrapperElementsAccessor
4189
    : public ElementsAccessorBase<Subclass, KindTraits> {
4190 4191
 public:
  explicit StringWrapperElementsAccessor(const char* name)
4192
      : ElementsAccessorBase<Subclass, KindTraits>(name) {
4193 4194 4195
    USE(KindTraits::Kind);
  }

4196 4197 4198 4199 4200
  static Handle<Object> GetInternalImpl(Handle<JSObject> holder,
                                        uint32_t entry) {
    return GetImpl(holder, entry);
  }

4201 4202 4203 4204 4205 4206
  static Handle<Object> GetImpl(Handle<JSObject> holder, uint32_t entry) {
    Isolate* isolate = holder->GetIsolate();
    Handle<String> string(GetString(*holder), isolate);
    uint32_t length = static_cast<uint32_t>(string->length());
    if (entry < length) {
      return isolate->factory()->LookupSingleCharacterStringFromCode(
4207
          String::Flatten(isolate, string)->Get(entry));
4208
    }
4209 4210 4211 4212 4213 4214 4215
    return BackingStoreAccessor::GetImpl(isolate, holder->elements(),
                                         entry - length);
  }

  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* elements,
                                uint32_t entry) {
    UNREACHABLE();
4216 4217 4218 4219 4220 4221 4222
  }

  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
    uint32_t length = static_cast<uint32_t>(GetString(holder)->length());
    if (entry < length) {
      PropertyAttributes attributes =
          static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
4223
      return PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
4224 4225 4226 4227
    }
    return BackingStoreAccessor::GetDetailsImpl(holder, entry - length);
  }

4228
  static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder,
4229 4230 4231 4232 4233
                                       FixedArrayBase* backing_store,
                                       uint32_t index, PropertyFilter filter) {
    uint32_t length = static_cast<uint32_t>(GetString(holder)->length());
    if (index < length) return index;
    uint32_t backing_store_entry = BackingStoreAccessor::GetEntryForIndexImpl(
4234
        isolate, holder, backing_store, index, filter);
4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259
    if (backing_store_entry == kMaxUInt32) return kMaxUInt32;
    DCHECK(backing_store_entry < kMaxUInt32 - length);
    return backing_store_entry + length;
  }

  static void DeleteImpl(Handle<JSObject> holder, uint32_t entry) {
    uint32_t length = static_cast<uint32_t>(GetString(*holder)->length());
    if (entry < length) {
      return;  // String contents can't be deleted.
    }
    BackingStoreAccessor::DeleteImpl(holder, entry - length);
  }

  static void SetImpl(Handle<JSObject> holder, uint32_t entry, Object* value) {
    uint32_t length = static_cast<uint32_t>(GetString(*holder)->length());
    if (entry < length) {
      return;  // String contents are read-only.
    }
    BackingStoreAccessor::SetImpl(holder->elements(), entry - length, value);
  }

  static void AddImpl(Handle<JSObject> object, uint32_t index,
                      Handle<Object> value, PropertyAttributes attributes,
                      uint32_t new_capacity) {
    DCHECK(index >= static_cast<uint32_t>(GetString(*object)->length()));
4260 4261 4262 4263 4264 4265
    // Explicitly grow fast backing stores if needed. Dictionaries know how to
    // extend their capacity themselves.
    if (KindTraits::Kind == FAST_STRING_WRAPPER_ELEMENTS &&
        (object->GetElementsKind() == SLOW_STRING_WRAPPER_ELEMENTS ||
         BackingStoreAccessor::GetCapacityImpl(*object, object->elements()) !=
             new_capacity)) {
4266
      GrowCapacityAndConvertImpl(object, new_capacity);
4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288
    }
    BackingStoreAccessor::AddImpl(object, index, value, attributes,
                                  new_capacity);
  }

  static void ReconfigureImpl(Handle<JSObject> object,
                              Handle<FixedArrayBase> store, uint32_t entry,
                              Handle<Object> value,
                              PropertyAttributes attributes) {
    uint32_t length = static_cast<uint32_t>(GetString(*object)->length());
    if (entry < length) {
      return;  // String contents can't be reconfigured.
    }
    BackingStoreAccessor::ReconfigureImpl(object, store, entry - length, value,
                                          attributes);
  }

  static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
                                              KeyAccumulator* accumulator,
                                              AddKeyConversion convert) {
    Isolate* isolate = receiver->GetIsolate();
    Handle<String> string(GetString(*receiver), isolate);
4289
    string = String::Flatten(isolate, string);
4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302
    uint32_t length = static_cast<uint32_t>(string->length());
    for (uint32_t i = 0; i < length; i++) {
      accumulator->AddKey(
          isolate->factory()->LookupSingleCharacterStringFromCode(
              string->Get(i)),
          convert);
    }
    BackingStoreAccessor::AddElementsToKeyAccumulatorImpl(receiver, accumulator,
                                                          convert);
  }

  static void CollectElementIndicesImpl(Handle<JSObject> object,
                                        Handle<FixedArrayBase> backing_store,
4303
                                        KeyAccumulator* keys) {
4304
    uint32_t length = GetString(*object)->length();
4305
    Factory* factory = keys->isolate()->factory();
4306
    for (uint32_t i = 0; i < length; i++) {
4307
      keys->AddKey(factory->NewNumberFromUint(i));
4308
    }
4309 4310
    BackingStoreAccessor::CollectElementIndicesImpl(object, backing_store,
                                                    keys);
4311 4312
  }

4313 4314
  static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
                                         uint32_t capacity) {
4315 4316
    Handle<FixedArrayBase> old_elements(object->elements(),
                                        object->GetIsolate());
4317
    ElementsKind from_kind = object->GetElementsKind();
4318 4319 4320 4321 4322
    if (from_kind == FAST_STRING_WRAPPER_ELEMENTS) {
      // The optimizing compiler relies on the prototype lookups of String
      // objects always returning undefined. If there's a store to the
      // initial String.prototype object, make sure all the optimizations
      // are invalidated.
4323
      object->GetIsolate()->UpdateNoElementsProtectorOnSetLength(object);
4324
    }
4325 4326 4327 4328 4329 4330 4331 4332 4333
    // This method should only be called if there's a reason to update the
    // elements.
    DCHECK(from_kind == SLOW_STRING_WRAPPER_ELEMENTS ||
           static_cast<uint32_t>(old_elements->length()) < capacity);
    Subclass::BasicGrowCapacityAndConvertImpl(object, old_elements, from_kind,
                                              FAST_STRING_WRAPPER_ELEMENTS,
                                              capacity);
  }

4334 4335 4336 4337
  static void CopyElementsImpl(Isolate* isolate, FixedArrayBase* from,
                               uint32_t from_start, FixedArrayBase* to,
                               ElementsKind from_kind, uint32_t to_start,
                               int packed_size, int copy_size) {
4338 4339
    DCHECK(!to->IsDictionary());
    if (from_kind == SLOW_STRING_WRAPPER_ELEMENTS) {
4340 4341
      CopyDictionaryToObjectElements(isolate, from, from_start, to,
                                     HOLEY_ELEMENTS, to_start, copy_size);
4342 4343
    } else {
      DCHECK_EQ(FAST_STRING_WRAPPER_ELEMENTS, from_kind);
4344
      CopyObjectToObjectElements(isolate, from, HOLEY_ELEMENTS, from_start, to,
4345
                                 HOLEY_ELEMENTS, to_start, copy_size);
4346
    }
4347 4348
  }

4349 4350 4351 4352 4353 4354 4355
  static uint32_t NumberOfElementsImpl(JSObject* object,
                                       FixedArrayBase* backing_store) {
    uint32_t length = GetString(object)->length();
    return length +
           BackingStoreAccessor::NumberOfElementsImpl(object, backing_store);
  }

4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373
 private:
  static String* GetString(JSObject* holder) {
    DCHECK(holder->IsJSValue());
    JSValue* js_value = JSValue::cast(holder);
    DCHECK(js_value->value()->IsString());
    return String::cast(js_value->value());
  }
};

class FastStringWrapperElementsAccessor
    : public StringWrapperElementsAccessor<
          FastStringWrapperElementsAccessor, FastHoleyObjectElementsAccessor,
          ElementsKindTraits<FAST_STRING_WRAPPER_ELEMENTS>> {
 public:
  explicit FastStringWrapperElementsAccessor(const char* name)
      : StringWrapperElementsAccessor<
            FastStringWrapperElementsAccessor, FastHoleyObjectElementsAccessor,
            ElementsKindTraits<FAST_STRING_WRAPPER_ELEMENTS>>(name) {}
4374

4375
  static Handle<NumberDictionary> NormalizeImpl(
4376 4377 4378
      Handle<JSObject> object, Handle<FixedArrayBase> elements) {
    return FastHoleyObjectElementsAccessor::NormalizeImpl(object, elements);
  }
4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389
};

class SlowStringWrapperElementsAccessor
    : public StringWrapperElementsAccessor<
          SlowStringWrapperElementsAccessor, DictionaryElementsAccessor,
          ElementsKindTraits<SLOW_STRING_WRAPPER_ELEMENTS>> {
 public:
  explicit SlowStringWrapperElementsAccessor(const char* name)
      : StringWrapperElementsAccessor<
            SlowStringWrapperElementsAccessor, DictionaryElementsAccessor,
            ElementsKindTraits<SLOW_STRING_WRAPPER_ELEMENTS>>(name) {}
4390 4391 4392 4393 4394

  static bool HasAccessorsImpl(JSObject* holder,
                               FixedArrayBase* backing_store) {
    return DictionaryElementsAccessor::HasAccessorsImpl(holder, backing_store);
  }
4395
};
4396

4397 4398 4399
}  // namespace


4400
void CheckArrayAbuse(Handle<JSObject> obj, const char* op, uint32_t index,
4401 4402
                     bool allow_appending) {
  DisallowHeapAllocation no_allocation;
4403
  Object* raw_length = nullptr;
4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418
  const char* elements_type = "array";
  if (obj->IsJSArray()) {
    JSArray* array = JSArray::cast(*obj);
    raw_length = array->length();
  } else {
    raw_length = Smi::FromInt(obj->elements()->length());
    elements_type = "object";
  }

  if (raw_length->IsNumber()) {
    double n = raw_length->Number();
    if (FastI2D(FastD2UI(n)) == n) {
      int32_t int32_length = DoubleToInt32(n);
      uint32_t compare_length = static_cast<uint32_t>(int32_length);
      if (allow_appending) compare_length++;
4419
      if (index >= compare_length) {
4420 4421
        PrintF("[OOB %s %s (%s length = %d, element accessed = %d) in ",
               elements_type, op, elements_type, static_cast<int>(int32_length),
4422
               static_cast<int>(index));
4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436
        TraceTopFrame(obj->GetIsolate());
        PrintF("]\n");
      }
    } else {
      PrintF("[%s elements length not integer value in ", elements_type);
      TraceTopFrame(obj->GetIsolate());
      PrintF("]\n");
    }
  } else {
    PrintF("[%s elements length not a number in ", elements_type);
    TraceTopFrame(obj->GetIsolate());
    PrintF("]\n");
  }
}
4437 4438


4439 4440
MaybeHandle<Object> ArrayConstructInitializeElements(Handle<JSArray> array,
                                                     Arguments* args) {
4441 4442 4443 4444 4445
  if (args->length() == 0) {
    // Optimize the case where there are no parameters passed.
    JSArray::Initialize(array, JSArray::kPreallocatedArrayElements);
    return array;

4446
  } else if (args->length() == 1 && args->at(0)->IsNumber()) {
4447
    uint32_t length;
4448
    if (!args->at(0)->ToArrayLength(&length)) {
4449 4450 4451 4452 4453
      return ThrowArrayLengthRangeError(array->GetIsolate());
    }

    // Optimize the case where there is one argument and the argument is a small
    // smi.
4454
    if (length > 0 && length < JSArray::kInitialMaxFastElementArray) {
4455 4456 4457
      ElementsKind elements_kind = array->GetElementsKind();
      JSArray::Initialize(array, length, length);

4458
      if (!IsHoleyElementsKind(elements_kind)) {
4459 4460
        elements_kind = GetHoleyElementsKind(elements_kind);
        JSObject::TransitionElementsKind(array, elements_kind);
4461
      }
4462 4463
    } else if (length == 0) {
      JSArray::Initialize(array, JSArray::kPreallocatedArrayElements);
4464 4465 4466 4467
    } else {
      // Take the argument as the length.
      JSArray::Initialize(array, 0);
      JSArray::SetLength(array, length);
4468
    }
4469
    return array;
4470 4471
  }

4472 4473
  Factory* factory = array->GetIsolate()->factory();

4474 4475
  // Set length and elements on the array.
  int number_of_elements = args->length();
4476 4477
  JSObject::EnsureCanContainElements(
      array, args, 0, number_of_elements, ALLOW_CONVERTED_DOUBLE_ELEMENTS);
4478 4479 4480

  // Allocate an appropriately typed elements array.
  ElementsKind elements_kind = array->GetElementsKind();
4481
  Handle<FixedArrayBase> elms;
4482
  if (IsDoubleElementsKind(elements_kind)) {
4483 4484
    elms = Handle<FixedArrayBase>::cast(
        factory->NewFixedDoubleArray(number_of_elements));
4485
  } else {
4486 4487
    elms = Handle<FixedArrayBase>::cast(
        factory->NewFixedArrayWithHoles(number_of_elements));
4488 4489 4490
  }

  // Fill in the content
4491
  switch (elements_kind) {
4492 4493
    case HOLEY_SMI_ELEMENTS:
    case PACKED_SMI_ELEMENTS: {
4494
      Handle<FixedArray> smi_elms = Handle<FixedArray>::cast(elms);
4495 4496
      for (int entry = 0; entry < number_of_elements; entry++) {
        smi_elms->set(entry, (*args)[entry], SKIP_WRITE_BARRIER);
4497 4498 4499
      }
      break;
    }
4500 4501
    case HOLEY_ELEMENTS:
    case PACKED_ELEMENTS: {
4502
      DisallowHeapAllocation no_gc;
4503
      WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
4504
      Handle<FixedArray> object_elms = Handle<FixedArray>::cast(elms);
4505 4506
      for (int entry = 0; entry < number_of_elements; entry++) {
        object_elms->set(entry, (*args)[entry], mode);
4507 4508 4509
      }
      break;
    }
4510 4511
    case HOLEY_DOUBLE_ELEMENTS:
    case PACKED_DOUBLE_ELEMENTS: {
4512 4513
      Handle<FixedDoubleArray> double_elms =
          Handle<FixedDoubleArray>::cast(elms);
4514 4515
      for (int entry = 0; entry < number_of_elements; entry++) {
        double_elms->set(entry, (*args)[entry]->Number());
4516 4517 4518 4519 4520 4521 4522 4523
      }
      break;
    }
    default:
      UNREACHABLE();
      break;
  }

4524
  array->set_elements(*elms);
4525 4526 4527 4528
  array->set_length(Smi::FromInt(number_of_elements));
  return array;
}

4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544
void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
                                               JSArray* source,
                                               JSTypedArray* destination,
                                               uintptr_t length,
                                               uintptr_t offset) {
  DCHECK(context->IsContext());
  DCHECK(source->IsJSArray());
  DCHECK(destination->IsJSTypedArray());

  switch (destination->GetElementsKind()) {
#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype, size)                       \
  case TYPE##_ELEMENTS:                                                        \
    CHECK(Fixed##Type##ElementsAccessor::TryCopyElementsFastNumber(            \
        context, source, destination, length, static_cast<uint32_t>(offset))); \
    break;
    TYPED_ARRAYS(TYPED_ARRAYS_CASE)
4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560
#undef TYPED_ARRAYS_CASE
    default:
      UNREACHABLE();
  }
}

void CopyTypedArrayElementsToTypedArray(JSTypedArray* source,
                                        JSTypedArray* destination,
                                        uintptr_t length, uintptr_t offset) {
  switch (destination->GetElementsKind()) {
#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype, size)             \
  case TYPE##_ELEMENTS:                                              \
    Fixed##Type##ElementsAccessor::CopyElementsFromTypedArray(       \
        source, destination, length, static_cast<uint32_t>(offset)); \
    break;
    TYPED_ARRAYS(TYPED_ARRAYS_CASE)
4561 4562 4563 4564 4565
#undef TYPED_ARRAYS_CASE
    default:
      UNREACHABLE();
  }
}
4566

4567 4568 4569 4570 4571 4572 4573
void CopyTypedArrayElementsSlice(JSTypedArray* source,
                                 JSTypedArray* destination, uintptr_t start,
                                 uintptr_t end) {
  destination->GetElementsAccessor()->CopyTypedArrayElementsSlice(
      source, destination, start, end);
}

4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588
void ElementsAccessor::InitializeOncePerProcess() {
  static ElementsAccessor* accessor_array[] = {
#define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind),
      ELEMENTS_LIST(ACCESSOR_ARRAY)
#undef ACCESSOR_ARRAY
  };

  STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) ==
                kElementsKindCount);

  elements_accessors_ = accessor_array;
}


void ElementsAccessor::TearDown() {
4589
  if (elements_accessors_ == nullptr) return;
4590 4591 4592
#define ACCESSOR_DELETE(Class, Kind, Store) delete elements_accessors_[Kind];
  ELEMENTS_LIST(ACCESSOR_DELETE)
#undef ACCESSOR_DELETE
4593
  elements_accessors_ = nullptr;
4594 4595
}

4596
Handle<JSArray> ElementsAccessor::Concat(Isolate* isolate, Arguments* args,
4597 4598
                                         uint32_t concat_size,
                                         uint32_t result_len) {
4599
  ElementsKind result_elements_kind = GetInitialFastElementsKind();
4600
  bool has_raw_doubles = false;
4601 4602
  {
    DisallowHeapAllocation no_gc;
4603
    bool is_holey = false;
4604
    for (uint32_t i = 0; i < concat_size; i++) {
4605 4606
      Object* arg = (*args)[i];
      ElementsKind arg_kind = JSArray::cast(arg)->GetElementsKind();
4607 4608
      has_raw_doubles = has_raw_doubles || IsDoubleElementsKind(arg_kind);
      is_holey = is_holey || IsHoleyElementsKind(arg_kind);
4609 4610
      result_elements_kind =
          GetMoreGeneralElementsKind(result_elements_kind, arg_kind);
4611 4612
    }
    if (is_holey) {
4613
      result_elements_kind = GetHoleyElementsKind(result_elements_kind);
4614 4615 4616 4617 4618 4619
    }
  }

  // If a double array is concatted into a fast elements array, the fast
  // elements array needs to be initialized to contain proper holes, since
  // boxing doubles may cause incremental marking.
4620
  bool requires_double_boxing =
4621
      has_raw_doubles && !IsDoubleElementsKind(result_elements_kind);
4622 4623 4624
  ArrayStorageAllocationMode mode = requires_double_boxing
                                        ? INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE
                                        : DONT_INITIALIZE_ARRAY_ELEMENTS;
4625
  Handle<JSArray> result_array = isolate->factory()->NewJSArray(
4626
      result_elements_kind, result_len, result_len, mode);
4627
  if (result_len == 0) return result_array;
4628 4629

  uint32_t insertion_index = 0;
4630
  Handle<FixedArrayBase> storage(result_array->elements(), isolate);
4631
  ElementsAccessor* accessor = ElementsAccessor::ForKind(result_elements_kind);
4632 4633 4634 4635
  for (uint32_t i = 0; i < concat_size; i++) {
    // It is crucial to keep |array| in a raw pointer form to avoid
    // performance degradation.
    JSArray* array = JSArray::cast((*args)[i]);
4636 4637 4638 4639 4640 4641
    uint32_t len = 0;
    array->length()->ToArrayLength(&len);
    if (len == 0) continue;
    ElementsKind from_kind = array->GetElementsKind();
    accessor->CopyElements(array, 0, from_kind, storage, insertion_index, len);
    insertion_index += len;
4642 4643
  }

4644
  DCHECK_EQ(insertion_index, result_len);
4645 4646 4647
  return result_array;
}

4648
ElementsAccessor** ElementsAccessor::elements_accessors_ = nullptr;
4649 4650
}  // namespace internal
}  // namespace v8