elements.cc 185 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/factory.h"
10
#include "src/frames.h"
11
#include "src/isolate-inl.h"
12
#include "src/messages.h"
13
#include "src/objects-inl.h"
14
#include "src/utils.h"
15 16 17 18 19 20 21 22

// 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)
23 24 25 26 27
//     - FastSmiOrObjectElementsAccessor
//       - FastPackedSmiElementsAccessor
//       - FastHoleySmiElementsAccessor
//       - FastPackedObjectElementsAccessor
//       - FastHoleyObjectElementsAccessor
28
//     - FastDoubleElementsAccessor
29 30
//       - FastPackedDoubleElementsAccessor
//       - FastHoleyDoubleElementsAccessor
31 32 33 34 35 36 37 38 39 40
//   - TypedElementsAccessor: template, with instantiations:
//     - FixedUint8ElementsAccessor
//     - FixedInt8ElementsAccessor
//     - FixedUint16ElementsAccessor
//     - FixedInt16ElementsAccessor
//     - FixedUint32ElementsAccessor
//     - FixedInt32ElementsAccessor
//     - FixedFloat32ElementsAccessor
//     - FixedFloat64ElementsAccessor
//     - FixedUint8ClampedElementsAccessor
41 42
//     - FixedBigUint64ElementsAccessor
//     - FixedBigInt64ElementsAccessor
43
//   - DictionaryElementsAccessor
44
//   - SloppyArgumentsElementsAccessor
45 46
//     - FastSloppyArgumentsElementsAccessor
//     - SlowSloppyArgumentsElementsAccessor
47 48 49
//   - StringWrapperElementsAccessor
//     - FastStringWrapperElementsAccessor
//     - SlowStringWrapperElementsAccessor
50

51 52 53 54
namespace v8 {
namespace internal {


55 56 57
namespace {


58 59
static const int kPackedSizeNotKnown = -1;

cbruni's avatar
cbruni committed
60 61
enum Where { AT_START, AT_END };

62

63 64 65 66 67
// 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.
68
#define ELEMENTS_LIST(V)                                                      \
69 70 71 72 73
  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,                 \
74
    FixedDoubleArray)                                                         \
75
  V(FastHoleyDoubleElementsAccessor, HOLEY_DOUBLE_ELEMENTS, FixedDoubleArray) \
76
  V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, NumberDictionary)        \
77 78 79 80
  V(FastSloppyArgumentsElementsAccessor, FAST_SLOPPY_ARGUMENTS_ELEMENTS,      \
    FixedArray)                                                               \
  V(SlowSloppyArgumentsElementsAccessor, SLOW_SLOPPY_ARGUMENTS_ELEMENTS,      \
    FixedArray)                                                               \
81 82 83 84
  V(FastStringWrapperElementsAccessor, FAST_STRING_WRAPPER_ELEMENTS,          \
    FixedArray)                                                               \
  V(SlowStringWrapperElementsAccessor, SLOW_STRING_WRAPPER_ELEMENTS,          \
    FixedArray)                                                               \
85 86 87 88 89 90 91 92 93
  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,                \
94 95 96
    FixedUint8ClampedArray)                                                   \
  V(FixedBigUint64ElementsAccessor, BIGUINT64_ELEMENTS, FixedBigUint64Array)  \
  V(FixedBigInt64ElementsAccessor, BIGINT64_ELEMENTS, FixedBigInt64Array)
97 98 99 100 101 102

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

103 104 105 106 107 108 109 110
#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;
111 112 113 114
ELEMENTS_LIST(ELEMENTS_TRAITS)
#undef ELEMENTS_TRAITS


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

121

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

  WriteBarrierMode write_barrier_mode =
154
      (IsObjectElementsKind(from_kind) && IsObjectElementsKind(to_kind))
155 156 157 158 159
          ? 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);
160 161 162 163
  }
}


164 165 166
static void CopyDictionaryToObjectElements(
    FixedArrayBase* from_base, uint32_t from_start, FixedArrayBase* to_base,
    ElementsKind to_kind, uint32_t to_start, int raw_copy_size) {
167
  DisallowHeapAllocation no_allocation;
168
  NumberDictionary* from = NumberDictionary::cast(from_base);
169 170
  int copy_size = raw_copy_size;
  if (raw_copy_size < 0) {
171
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
172 173 174
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
    copy_size = from->max_number_key() + 1 - from_start;
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
175
      int start = to_start + copy_size;
176
      int length = to_base->length() - start;
177 178
      if (length > 0) {
        Heap* heap = from->GetHeap();
179
        MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
180
                      heap->the_hole_value(), length);
181 182 183
      }
    }
  }
184
  DCHECK(to_base != from_base);
185
  DCHECK(IsSmiOrObjectElementsKind(to_kind));
186
  if (copy_size == 0) return;
187
  FixedArray* to = FixedArray::cast(to_base);
188 189 190 191
  uint32_t to_length = to->length();
  if (to_start + copy_size > to_length) {
    copy_size = to_length - to_start;
  }
192 193
  WriteBarrierMode write_barrier_mode =
      IsObjectElementsKind(to_kind) ? UPDATE_WRITE_BARRIER : SKIP_WRITE_BARRIER;
194
  Isolate* isolate = from->GetIsolate();
195
  for (int i = 0; i < copy_size; i++) {
196
    int entry = from->FindEntry(isolate, i + from_start);
197
    if (entry != NumberDictionary::kNotFound) {
198
      Object* value = from->ValueAt(entry);
199
      DCHECK(!value->IsTheHole(isolate));
200
      to->set(i + to_start, value, write_barrier_mode);
201
    } else {
202
      to->set_the_hole(isolate, i + to_start);
203 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.
static void CopyDoubleToObjectElements(FixedArrayBase* from_base,
212
                                       uint32_t from_start,
213
                                       FixedArrayBase* to_base,
214
                                       uint32_t to_start, int raw_copy_size) {
215 216
  int copy_size = raw_copy_size;
  if (raw_copy_size < 0) {
217
    DisallowHeapAllocation no_allocation;
218
    DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd ||
219
           raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
220 221
    copy_size = Min(from_base->length() - from_start,
                    to_base->length() - to_start);
222
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
223 224 225 226
      // 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;
227
      int length = to_base->length() - start;
228
      if (length > 0) {
229
        Heap* heap = from_base->GetHeap();
230
        MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
231
                      heap->the_hole_value(), length);
232 233
      }
    }
234
  }
235

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

  // From here on, the code below could actually allocate. Therefore the raw
  // values are wrapped into handles.
242
  Isolate* isolate = from_base->GetIsolate();
243 244
  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(from->GetIsolate()));
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
static void CopyDictionaryToDoubleElements(FixedArrayBase* from_base,
403
                                           uint32_t from_start,
404
                                           FixedArrayBase* to_base,
405 406
                                           uint32_t to_start,
                                           int raw_copy_size) {
407
  DisallowHeapAllocation no_allocation;
408
  NumberDictionary* from = NumberDictionary::cast(from_base);
409 410
  int copy_size = raw_copy_size;
  if (copy_size < 0) {
411
    DCHECK(copy_size == ElementsAccessor::kCopyToEnd ||
412 413 414
           copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
    copy_size = from->max_number_key() + 1 - from_start;
    if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
415
      for (int i = to_start + copy_size; i < to_base->length(); ++i) {
416
        FixedDoubleArray::cast(to_base)->set_the_hole(i);
417 418 419 420
      }
    }
  }
  if (copy_size == 0) return;
421
  FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
422 423 424 425
  uint32_t to_length = to->length();
  if (to_start + copy_size > to_length) {
    copy_size = to_length - to_start;
  }
426
  Isolate* isolate = from->GetIsolate();
427
  for (int i = 0; i < copy_size; i++) {
428
    int entry = from->FindEntry(isolate, i + from_start);
429
    if (entry != NumberDictionary::kNotFound) {
430 431 432 433 434 435 436
      to->set(i + to_start, from->ValueAt(entry)->Number());
    } else {
      to->set_the_hole(i + to_start);
    }
  }
}

437 438
static void TraceTopFrame(Isolate* isolate) {
  StackFrameIterator it(isolate);
439 440 441 442 443 444
  if (it.done()) {
    PrintF("unknown location (no JavaScript frames present)");
    return;
  }
  StackFrame* raw_frame = it.frame();
  if (raw_frame->is_internal()) {
445 446
    Code* apply_builtin =
        isolate->builtins()->builtin(Builtins::kFunctionPrototypeApply);
447 448 449 450 451 452
    if (raw_frame->unchecked_code() == apply_builtin) {
      PrintF("apply from ");
      it.Advance();
      raw_frame = it.frame();
    }
  }
453
  JavaScriptFrame::PrintTop(isolate, stdout, false, true);
454 455
}

456 457 458 459
static void SortIndices(
    Handle<FixedArray> indices, uint32_t sort_size,
    WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER) {
  struct {
460 461 462 463
    bool operator()(const base::AtomicElement<Object*>& elementA,
                    const base::AtomicElement<Object*>& elementB) {
      const Object* a = elementA.value();
      const Object* b = elementB.value();
464 465 466 467
      if (a->IsSmi() || !a->IsUndefined(HeapObject::cast(a)->GetIsolate())) {
        if (!b->IsSmi() && b->IsUndefined(HeapObject::cast(b)->GetIsolate())) {
          return true;
        }
468 469
        return a->Number() < b->Number();
      }
470
      return !b->IsSmi() && b->IsUndefined(HeapObject::cast(b)->GetIsolate());
471 472
    }
  } cmp;
473 474 475 476 477
  // 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());
478 479 480 481 482 483
  std::sort(start, start + sort_size, cmp);
  if (write_barrier_mode != SKIP_WRITE_BARRIER) {
    FIXED_ARRAY_ELEMENTS_WRITE_BARRIER(indices->GetIsolate()->heap(), *indices,
                                       0, sort_size);
  }
}
484

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
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);
}

506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
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);
}

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
// 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;
};

541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
// 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).
558
template <typename Subclass, typename ElementsTraitsParam>
559
class ElementsAccessorBase : public InternalElementsAccessor {
560
 public:
561
  explicit ElementsAccessorBase(const char* name)
562
      : InternalElementsAccessor(name) {}
563 564 565 566

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

567
  static ElementsKind kind() { return ElementsTraits::Kind; }
568

569
  static void ValidateContents(JSObject* holder, int length) {}
570

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

588
  void Validate(JSObject* holder) final {
589
    DisallowHeapAllocation no_gc;
590
    Subclass::ValidateImpl(holder);
591 592
  }

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

607
  static void TryTransitionResultArrayToPacked(Handle<JSArray> array) {
608
    if (!IsHoleyOrDictionaryElementsKind(kind())) return;
609
    Handle<FixedArrayBase> backing_store(array->elements());
jgruber's avatar
jgruber committed
610
    int length = Smi::ToInt(array->length());
611
    if (!Subclass::IsPackedImpl(*array, *backing_store, 0, length)) {
612 613 614 615 616 617 618 619 620 621 622 623
      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);
    }
  }

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

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

637 638 639 640 641 642 643 644 645 646
  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();
  }

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

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

656
  Handle<Object> Get(Handle<JSObject> holder, uint32_t entry) final {
657
    return Subclass::GetInternalImpl(holder, entry);
658 659
  }

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

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

671
  void Set(Handle<JSObject> holder, uint32_t entry, Object* value) final {
672
    Subclass::SetImpl(holder, entry, value);
673 674
  }

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

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

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

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

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

704
  static uint32_t PushImpl(Handle<JSArray> receiver, Arguments* args,
705
                           uint32_t push_sized) {
706 707 708
    UNREACHABLE();
  }

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

714
  static uint32_t UnshiftImpl(Handle<JSArray> receiver, Arguments* args,
715 716 717 718
                              uint32_t unshift_size) {
    UNREACHABLE();
  }

719 720
  Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
                         uint32_t end) final {
721
    return Subclass::SliceImpl(receiver, start, end);
722 723
  }

724 725
  static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
                                    uint32_t end) {
726
    UNREACHABLE();
727 728
  }

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

  static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver,
                                    uint32_t start, uint32_t delete_count,
737
                                    Arguments* args, uint32_t add_count) {
738 739 740
    UNREACHABLE();
  }

741
  Handle<Object> Pop(Handle<JSArray> receiver) final {
742
    return Subclass::PopImpl(receiver);
cbruni's avatar
cbruni committed
743 744
  }

745
  static Handle<Object> PopImpl(Handle<JSArray> receiver) {
cbruni's avatar
cbruni committed
746 747
    UNREACHABLE();
  }
748

749
  Handle<Object> Shift(Handle<JSArray> receiver) final {
750
    return Subclass::ShiftImpl(receiver);
751 752
  }

753
  static Handle<Object> ShiftImpl(Handle<JSArray> receiver) {
754 755 756
    UNREACHABLE();
  }

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

762 763
  static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
                            uint32_t length,
764 765 766 767 768 769 770 771
                            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();
772
      if (!IsHoleyElementsKind(kind)) {
773 774 775 776 777 778 779
        kind = GetHoleyElementsKind(kind);
        JSObject::TransitionElementsKind(array, kind);
      }
    }

    // Check whether the backing store should be shrunk.
    uint32_t capacity = backing_store->length();
780
    old_length = Min(old_length, capacity);
781 782 783
    if (length == 0) {
      array->initialize_elements();
    } else if (length <= capacity) {
784
      if (IsSmiOrObjectElementsKind(kind())) {
785 786 787 788
        JSObject::EnsureWritableFastElements(array);
        if (array->elements() != *backing_store) {
          backing_store = handle(array->elements(), isolate);
        }
789
      }
790
      if (2 * length + JSObject::kMinAddedElementsCapacity <= capacity) {
791
        // If more than half the elements won't be used, trim the array.
792 793 794 795 796 797 798
        // 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);
799 800 801 802
        // Fill the non-trimmed elements with holes.
        BackingStore::cast(*backing_store)
            ->FillWithHoles(length,
                            std::min(old_length, capacity - elements_to_trim));
803 804
      } else {
        // Otherwise, fill the unused tail with holes.
805
        BackingStore::cast(*backing_store)->FillWithHoles(length, old_length);
806 807 808 809
      }
    } else {
      // Check whether the backing store should be expanded.
      capacity = Max(length, JSObject::NewElementsCapacity(capacity));
810
      Subclass::GrowCapacityAndConvertImpl(array, capacity);
811 812 813
    }

    array->set_length(Smi::FromInt(length));
814
    JSObject::ValidateElements(*array);
815
  }
816

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

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

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

835 836 837 838 839
  static uint32_t GetMaxNumberOfEntries(JSObject* receiver,
                                        FixedArrayBase* elements) {
    return Subclass::GetMaxIndex(receiver, elements);
  }

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

  static Handle<FixedArrayBase> ConvertElementsWithCapacity(
      Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
      ElementsKind from_kind, uint32_t capacity, int copy_size) {
851 852 853 854 855 856 857 858
    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) {
859
    Isolate* isolate = object->GetIsolate();
860
    Handle<FixedArrayBase> new_elements;
861
    if (IsDoubleElementsKind(kind())) {
862
      new_elements = isolate->factory()->NewFixedDoubleArray(capacity);
863
    } else {
864
      new_elements = isolate->factory()->NewUninitializedFixedArray(capacity);
865 866
    }

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

872 873
    Subclass::CopyElementsImpl(*old_elements, src_index, *new_elements,
                               from_kind, dst_index, packed_size, copy_size);
874 875

    return new_elements;
876 877
  }

878 879 880 881 882
  static void TransitionElementsKindImpl(Handle<JSObject> object,
                                         Handle<Map> to_map) {
    Handle<Map> from_map = handle(object->map());
    ElementsKind from_kind = from_map->elements_kind();
    ElementsKind to_kind = to_map->elements_kind();
883
    if (IsHoleyElementsKind(from_kind)) {
884 885 886 887 888 889 890 891 892 893
      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);

      Handle<FixedArrayBase> from_elements(object->elements());
      if (object->elements() == object->GetHeap()->empty_fixed_array() ||
894
          IsDoubleElementsKind(from_kind) == IsDoubleElementsKind(to_kind)) {
895 896 897 898
        // No change is needed to the elements() buffer, the transition
        // only requires a map change.
        JSObject::MigrateToMap(object, to_map);
      } else {
899 900 901
        DCHECK(
            (IsSmiElementsKind(from_kind) && IsDoubleElementsKind(to_kind)) ||
            (IsDoubleElementsKind(from_kind) && IsObjectElementsKind(to_kind)));
902 903 904 905 906 907 908 909 910 911 912 913 914
        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) {
        JSObject::PrintElementsTransition(stdout, object, from_kind,
                                          from_elements, to_kind,
                                          handle(object->elements()));
      }
    }
  }

915
  static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
916
                                         uint32_t capacity) {
917
    ElementsKind from_kind = object->GetElementsKind();
918
    if (IsSmiOrObjectElementsKind(from_kind)) {
919 920 921
      // 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.
922
      object->GetIsolate()->UpdateNoElementsProtectorOnSetLength(object);
923 924 925 926
    }
    Handle<FixedArrayBase> old_elements(object->elements());
    // This method should only be called if there's a reason to update the
    // elements.
927
    DCHECK(IsDoubleElementsKind(from_kind) != IsDoubleElementsKind(kind()) ||
928 929
           IsDictionaryElementsKind(from_kind) ||
           static_cast<uint32_t>(old_elements->length()) < capacity);
930 931 932 933 934 935 936
    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) {
937 938 939
    Handle<FixedArrayBase> elements =
        ConvertElementsWithCapacity(object, old_elements, from_kind, capacity);

940 941
    if (IsHoleyOrDictionaryElementsKind(from_kind))
      to_kind = GetHoleyElementsKind(to_kind);
942 943 944 945 946 947 948 949 950 951
    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);
    }
952 953
  }

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

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

963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
  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;
    }
    Handle<FixedArrayBase> old_elements(object->elements());
    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
  static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
                               FixedArrayBase* to, ElementsKind from_kind,
                               uint32_t to_start, int packed_size,
994
                               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, from_start, *to, from_kind, to_start,
                               packed_size, copy_size);
1022 1023
  }

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

1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
  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();
  }

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

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

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

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

1063 1064 1065 1066
  Maybe<bool> CollectValuesOrEntries(Isolate* isolate, Handle<JSObject> object,
                                     Handle<FixedArray> values_or_entries,
                                     bool get_entries, int* nof_items,
                                     PropertyFilter filter) {
1067
    return Subclass::CollectValuesOrEntriesImpl(
1068 1069 1070 1071 1072 1073 1074
        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) {
1075
    DCHECK_EQ(*nof_items, 0);
1076 1077
    KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
                               ALL_PROPERTIES);
1078
    Subclass::CollectElementIndicesImpl(
1079
        object, handle(object->elements(), isolate), &accumulator);
1080 1081
    Handle<FixedArray> keys = accumulator.GetKeys();

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

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

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

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

    // 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;
1126
      }
1127 1128 1129 1130 1131 1132 1133

      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);
1134 1135 1136 1137 1138 1139 1140
      values_or_entries->set(count++, *value);
    }

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

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

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

1165 1166 1167
  static Handle<FixedArray> DirectCollectElementIndicesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
1168 1169
      PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices,
      uint32_t insertion_index = 0) {
1170
    uint32_t length = Subclass::GetMaxIndex(*object, *backing_store);
1171
    for (uint32_t i = 0; i < length; i++) {
1172 1173
      if (Subclass::HasElementImpl(isolate, *object, i, *backing_store,
                                   filter)) {
1174
        if (convert == GetKeysConversion::kConvertToString) {
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
          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;
  }

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

1195
  static MaybeHandle<FixedArray> PrependElementIndicesImpl(
1196 1197 1198 1199 1200 1201
      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 =
1202
        Subclass::GetMaxNumberOfEntries(*object, *backing_store);
1203

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

    // Collect the element indices into a new list.
1212 1213 1214 1215 1216 1217 1218 1219
    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)) {
1220
      if (IsHoleyOrDictionaryElementsKind(kind())) {
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
        // 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);
    }

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

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

    // Copy over the passed-in property keys.
1254 1255
    CopyObjectToObjectElements(*keys, PACKED_ELEMENTS, 0, *combined_keys,
                               PACKED_ELEMENTS, nof_indices, nof_property_keys);
1256

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

    return combined_keys;
  }
1269

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

1276 1277
  static uint32_t GetCapacityImpl(JSObject* holder,
                                  FixedArrayBase* backing_store) {
1278
    return backing_store->length();
1279 1280
  }

1281
  uint32_t GetCapacity(JSObject* holder, FixedArrayBase* backing_store) final {
1282
    return Subclass::GetCapacityImpl(holder, backing_store);
1283 1284
  }

1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
  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);
  }

1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309
  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);
  }

1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323
  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);
  }

1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
  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);
  }

1337 1338 1339 1340
  static void ReverseImpl(JSObject* receiver) { UNREACHABLE(); }

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

1341 1342 1343
  static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store,
                                       uint32_t entry) {
    return entry;
1344 1345
  }

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

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

  static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
1369
                                        uint32_t entry) {
1370
    return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
1371 1372
  }

1373
  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
1374
    return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
1375 1376 1377
  }

  PropertyDetails GetDetails(JSObject* holder, uint32_t entry) final {
1378
    return Subclass::GetDetailsImpl(holder, entry);
1379 1380
  }

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

1387 1388 1389
  static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
                                                        Handle<JSObject> object,
                                                        uint32_t length) {
1390 1391 1392
    UNREACHABLE();
  }

1393 1394 1395 1396 1397
 private:
  DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase);
};


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

1406 1407 1408 1409 1410 1411 1412
  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) {
1413 1414 1415 1416 1417
    return NumberOfElementsImpl(receiver, backing_store);
  }

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

1422 1423
  static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
                            uint32_t length,
1424
                            Handle<FixedArrayBase> backing_store) {
1425 1426
    Handle<NumberDictionary> dict =
        Handle<NumberDictionary>::cast(backing_store);
1427 1428 1429
    int capacity = dict->Capacity();
    uint32_t old_length = 0;
    CHECK(array->length()->ToArrayLength(&old_length));
1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
    {
      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;
              }
1444 1445 1446 1447
            }
          }
        }

1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
        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++;
              }
1462 1463 1464
            }
          }

1465 1466 1467
          // Update the number of elements.
          dict->ElementsRemoved(removed_entries);
        }
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
      }
    }

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

  static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
                               FixedArrayBase* to, ElementsKind from_kind,
                               uint32_t to_start, int packed_size,
                               int copy_size) {
    UNREACHABLE();
  }

1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
  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));
1492 1493
    Handle<NumberDictionary> source_dict(
        NumberDictionary::cast(receiver->elements()));
1494 1495 1496
    int entry_count = source_dict->Capacity();
    for (int i = 0; i < entry_count; i++) {
      Object* key = source_dict->KeyAt(i);
1497 1498 1499 1500 1501 1502 1503 1504 1505 1506
      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(
            NumberDictionary::cast(result_array->elements()));
        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);
1507 1508 1509 1510 1511
      }
    }

    return result_array;
  }
1512

1513
  static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
1514 1515
    Handle<NumberDictionary> dict(NumberDictionary::cast(obj->elements()));
    dict = NumberDictionary::DeleteEntry(dict, entry);
1516
    obj->set_elements(*dict);
1517 1518
  }

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

1535
  static Object* GetRaw(FixedArrayBase* store, uint32_t entry) {
1536
    NumberDictionary* backing_store = NumberDictionary::cast(store);
1537 1538 1539
    return backing_store->ValueAt(entry);
  }

1540 1541 1542
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* backing_store,
                                uint32_t entry) {
    return handle(GetRaw(backing_store, entry), isolate);
1543 1544 1545
  }

  static inline void SetImpl(Handle<JSObject> holder, uint32_t entry,
1546
                             Object* value) {
1547 1548 1549 1550 1551
    SetImpl(holder->elements(), entry, value);
  }

  static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry,
                             Object* value) {
1552
    NumberDictionary::cast(backing_store)->ValueAtPut(entry, value);
1553 1554 1555
  }

  static void ReconfigureImpl(Handle<JSObject> object,
1556
                              Handle<FixedArrayBase> store, uint32_t entry,
1557 1558
                              Handle<Object> value,
                              PropertyAttributes attributes) {
1559
    NumberDictionary* dictionary = NumberDictionary::cast(*store);
1560
    if (attributes != NONE) object->RequireSlowElements(dictionary);
1561 1562
    dictionary->ValueAtPut(entry, *value);
    PropertyDetails details = dictionary->DetailsAt(entry);
1563 1564 1565
    details = PropertyDetails(kData, attributes, PropertyCellType::kNoCell,
                              details.dictionary_index());

1566
    dictionary->DetailsAtPut(entry, details);
1567 1568
  }

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

1585 1586
  static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* store,
                           uint32_t entry) {
1587
    DisallowHeapAllocation no_gc;
1588
    NumberDictionary* dict = NumberDictionary::cast(store);
1589
    Object* index = dict->KeyAt(entry);
1590
    return !index->IsTheHole(isolate);
1591 1592
  }

1593
  static uint32_t GetIndexForEntryImpl(FixedArrayBase* store, uint32_t entry) {
1594
    DisallowHeapAllocation no_gc;
1595
    NumberDictionary* dict = NumberDictionary::cast(store);
1596
    uint32_t result = 0;
1597
    CHECK(dict->KeyAt(entry)->ToArrayIndex(&result));
1598 1599 1600
    return result;
  }

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

1616 1617 1618 1619
  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
    return GetDetailsImpl(holder->elements(), entry);
  }

1620
  static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
1621
                                        uint32_t entry) {
1622
    return NumberDictionary::cast(backing_store)->DetailsAt(entry);
1623
  }
1624

1625 1626
  static uint32_t FilterKey(Handle<NumberDictionary> dictionary, int entry,
                            Object* raw_key, PropertyFilter filter) {
1627 1628 1629 1630 1631
    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;
1632
    return static_cast<uint32_t>(raw_key->Number());
1633 1634
  }

1635
  static uint32_t GetKeyForEntryImpl(Isolate* isolate,
1636
                                     Handle<NumberDictionary> dictionary,
1637 1638 1639
                                     int entry, PropertyFilter filter) {
    DisallowHeapAllocation no_gc;
    Object* raw_key = dictionary->KeyAt(entry);
1640
    if (!dictionary->IsKey(isolate, raw_key)) return kMaxUInt32;
1641 1642 1643
    return FilterKey(dictionary, entry, raw_key, filter);
  }

1644 1645
  static void CollectElementIndicesImpl(Handle<JSObject> object,
                                        Handle<FixedArrayBase> backing_store,
1646 1647
                                        KeyAccumulator* keys) {
    if (keys->filter() & SKIP_STRINGS) return;
1648
    Isolate* isolate = keys->isolate();
1649 1650
    Handle<NumberDictionary> dictionary =
        Handle<NumberDictionary>::cast(backing_store);
1651
    int capacity = dictionary->Capacity();
1652 1653
    Handle<FixedArray> elements = isolate->factory()->NewFixedArray(
        GetMaxNumberOfEntries(*object, *backing_store));
1654
    int insertion_index = 0;
1655
    PropertyFilter filter = keys->filter();
1656
    for (int i = 0; i < capacity; i++) {
1657 1658 1659 1660
      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) {
1661
        keys->AddShadowingKey(raw_key);
1662 1663 1664
        continue;
      }
      elements->set(insertion_index, raw_key);
1665 1666 1667 1668 1669
      insertion_index++;
    }
    SortIndices(elements, insertion_index);
    for (int i = 0; i < insertion_index; i++) {
      keys->AddKey(elements->get(i));
1670 1671
    }
  }
1672

1673 1674 1675
  static Handle<FixedArray> DirectCollectElementIndicesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
1676 1677
      PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices,
      uint32_t insertion_index = 0) {
1678 1679
    if (filter & SKIP_STRINGS) return list;
    if (filter & ONLY_ALL_CAN_READ) return list;
1680

1681 1682
    Handle<NumberDictionary> dictionary =
        Handle<NumberDictionary>::cast(backing_store);
1683 1684
    uint32_t capacity = dictionary->Capacity();
    for (uint32_t i = 0; i < capacity; i++) {
1685
      uint32_t key = GetKeyForEntryImpl(isolate, dictionary, i, filter);
1686 1687 1688 1689 1690 1691 1692 1693 1694
      if (key == kMaxUInt32) continue;
      Handle<Object> index = isolate->factory()->NewNumberFromUint(key);
      list->set(insertion_index, *index);
      insertion_index++;
    }
    *nof_indices = insertion_index;
    return list;
  }

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

  static bool IncludesValueFastPath(Isolate* isolate, Handle<JSObject> receiver,
                                    Handle<Object> value, uint32_t start_from,
                                    uint32_t length, Maybe<bool>* result) {
    DisallowHeapAllocation no_gc;
1717
    NumberDictionary* dictionary = NumberDictionary::cast(receiver->elements());
1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734
    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;
      }

1735
      if (dictionary->DetailsAt(i).kind() == kAccessor) {
1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762
        // 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;
      }
    }
1763 1764
    ElementsKind original_elements_kind = receiver->GetElementsKind();
    USE(original_elements_kind);
1765 1766
    Handle<NumberDictionary> dictionary(
        NumberDictionary::cast(receiver->elements()), isolate);
1767 1768 1769
    // Iterate through entire range, as accessing elements out of order is
    // observable
    for (uint32_t k = start_from; k < length; ++k) {
1770
      DCHECK_EQ(receiver->GetElementsKind(), original_elements_kind);
1771
      int entry = dictionary->FindEntry(isolate, k);
1772
      if (entry == NumberDictionary::kNotFound) {
1773 1774 1775 1776
        if (search_for_hole) return Just(true);
        continue;
      }

1777
      PropertyDetails details = GetDetailsImpl(*dictionary, entry);
1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796
      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);

1797
          // Bailout to slow path if elements on prototype changed
1798 1799 1800 1801
          if (!JSObject::PrototypeHasNoElements(isolate, *receiver)) {
            return IncludesValueSlowPath(isolate, receiver, value, k + 1,
                                         length);
          }
1802 1803 1804 1805 1806

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

          // Otherwise, bailout or update elements
1807 1808 1809 1810 1811 1812 1813 1814

          // 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.
1815
          if (receiver->GetElementsKind() != DICTIONARY_ELEMENTS) {
1816 1817 1818
            ElementsAccessor* accessor = receiver->GetElementsAccessor();
            return accessor->IncludesValue(isolate, receiver, value, k + 1,
                                           length);
1819
          }
1820 1821
          dictionary =
              handle(NumberDictionary::cast(receiver->elements()), isolate);
1822 1823 1824 1825 1826 1827
          break;
        }
      }
    }
    return Just(false);
  }
1828 1829 1830 1831 1832 1833 1834

  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));

1835 1836
    ElementsKind original_elements_kind = receiver->GetElementsKind();
    USE(original_elements_kind);
1837 1838
    Handle<NumberDictionary> dictionary(
        NumberDictionary::cast(receiver->elements()), isolate);
1839 1840 1841
    // Iterate through entire range, as accessing elements out of order is
    // observable.
    for (uint32_t k = start_from; k < length; ++k) {
1842
      DCHECK_EQ(receiver->GetElementsKind(), original_elements_kind);
1843
      int entry = dictionary->FindEntry(isolate, k);
1844
      if (entry == NumberDictionary::kNotFound) continue;
1845 1846 1847 1848 1849 1850 1851 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

      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);
          }
1883 1884
          dictionary =
              handle(NumberDictionary::cast(receiver->elements()), isolate);
1885 1886 1887 1888 1889 1890
          break;
        }
      }
    }
    return Just<int64_t>(-1);
  }
1891 1892 1893 1894 1895 1896 1897

  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();
1898
    NumberDictionary* dictionary = NumberDictionary::cast(holder->elements());
1899 1900 1901 1902 1903 1904 1905 1906
    // 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());
1907
      if (k->Number() > NumberDictionary::kRequiresSlowElementsLimit) {
1908 1909
        requires_slow_elements = true;
      } else {
jgruber's avatar
jgruber committed
1910
        max_key = Max(max_key, Smi::ToInt(k));
1911 1912 1913 1914 1915 1916 1917 1918 1919
      }
    }
    if (requires_slow_elements) {
      DCHECK(dictionary->requires_slow_elements());
    } else if (!dictionary->requires_slow_elements()) {
      DCHECK_LE(max_key, dictionary->max_number_key());
    }
#endif
  }
1920 1921
};

1922

1923
// Super class for all fast element arrays.
1924 1925
template <typename Subclass, typename KindTraits>
class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
1926 1927
 public:
  explicit FastElementsAccessor(const char* name)
1928
      : ElementsAccessorBase<Subclass, KindTraits>(name) {}
1929

1930
  typedef typename KindTraits::BackingStore BackingStore;
1931

1932 1933
  static Handle<NumberDictionary> NormalizeImpl(Handle<JSObject> object,
                                                Handle<FixedArrayBase> store) {
1934
    Isolate* isolate = store->GetIsolate();
1935
    ElementsKind kind = Subclass::kind();
1936 1937 1938

    // Ensure that notifications fire if the array or object prototypes are
    // normalizing.
1939 1940
    if (IsSmiOrObjectElementsKind(kind) ||
        kind == FAST_STRING_WRAPPER_ELEMENTS) {
1941
      isolate->UpdateNoElementsProtectorOnNormalizeElements(object);
1942 1943 1944
    }

    int capacity = object->GetFastElementsUsage();
1945 1946
    Handle<NumberDictionary> dictionary =
        NumberDictionary::New(isolate, capacity);
1947 1948 1949

    PropertyDetails details = PropertyDetails::Empty();
    int j = 0;
1950
    int max_number_key = -1;
1951
    for (int i = 0; j < capacity; i++) {
1952
      if (IsHoleyOrDictionaryElementsKind(kind)) {
1953
        if (BackingStore::cast(*store)->is_the_hole(isolate, i)) continue;
1954
      }
1955
      max_number_key = i;
1956
      Handle<Object> value = Subclass::GetImpl(isolate, *store, i);
1957
      dictionary = NumberDictionary::Add(dictionary, i, value, details);
1958 1959
      j++;
    }
1960 1961 1962 1963 1964

    if (max_number_key > 0) {
      dictionary->UpdateMaxNumberKey(static_cast<uint32_t>(max_number_key),
                                     object);
    }
1965 1966 1967
    return dictionary;
  }

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

1987
    isolate->heap()->RightTrimFixedArray(*backing_store, length - entry);
1988 1989
  }

1990
  static void DeleteCommon(Handle<JSObject> obj, uint32_t entry,
1991
                           Handle<FixedArrayBase> store) {
1992
    DCHECK(obj->HasSmiOrObjectElements() || obj->HasDoubleElements() ||
1993 1994
           obj->HasFastArgumentsElements() ||
           obj->HasFastStringWrapperElements());
1995
    Handle<BackingStore> backing_store = Handle<BackingStore>::cast(store);
1996 1997 1998 1999 2000 2001
    if (!obj->IsJSArray() &&
        entry == static_cast<uint32_t>(store->length()) - 1) {
      DeleteAtEnd(obj, backing_store, entry);
      return;
    }

2002
    Isolate* isolate = obj->GetIsolate();
2003
    backing_store->set_the_hole(isolate, entry);
2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015

    // 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;
    if (backing_store->GetHeap()->InNewSpace(*backing_store)) return;
    uint32_t length = 0;
    if (obj->IsJSArray()) {
      JSArray::cast(*obj)->length()->ToArrayLength(&length);
    } else {
      length = static_cast<uint32_t>(store->length());
2016
    }
2017 2018 2019 2020 2021 2022 2023 2024

    // 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 >=
2025 2026
                  NumberDictionary::kEntrySize *
                      NumberDictionary::kPreferFastElementsSizeFactor);
2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038
    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;
2039
      }
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049
      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.
2050 2051 2052
        if (NumberDictionary::kPreferFastElementsSizeFactor *
                NumberDictionary::ComputeCapacity(num_used) *
                NumberDictionary::kEntrySize >
2053 2054
            static_cast<uint32_t>(backing_store->length())) {
          return;
2055
        }
2056 2057
      }
    }
2058
    JSObject::NormalizeElements(obj);
2059 2060
  }

2061
  static void ReconfigureImpl(Handle<JSObject> object,
2062
                              Handle<FixedArrayBase> store, uint32_t entry,
2063 2064
                              Handle<Object> value,
                              PropertyAttributes attributes) {
2065
    Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(object);
2066 2067
    entry = dictionary->FindEntry(entry);
    DictionaryElementsAccessor::ReconfigureImpl(object, dictionary, entry,
2068
                                                value, attributes);
2069 2070
  }

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

2094
  static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
2095 2096 2097 2098
    ElementsKind kind = KindTraits::Kind;
    if (IsFastPackedElementsKind(kind)) {
      JSObject::TransitionElementsKind(obj, GetHoleyElementsKind(kind));
    }
2099
    if (IsSmiOrObjectElementsKind(KindTraits::Kind)) {
2100 2101
      JSObject::EnsureWritableFastElements(obj);
    }
2102
    DeleteCommon(obj, entry, handle(obj->elements()));
2103 2104
  }

2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119
  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;
2120 2121
  }

2122 2123 2124
  static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
                                              KeyAccumulator* accumulator,
                                              AddKeyConversion convert) {
2125 2126
    Isolate* isolate = accumulator->isolate();
    Handle<FixedArrayBase> elements(receiver->elements(), isolate);
2127
    uint32_t length = Subclass::GetMaxNumberOfEntries(*receiver, *elements);
2128 2129
    for (uint32_t i = 0; i < length; i++) {
      if (IsFastPackedElementsKind(KindTraits::Kind) ||
2130
          HasEntryImpl(isolate, *elements, i)) {
2131
        accumulator->AddKey(Subclass::GetImpl(isolate, *elements, i), convert);
2132 2133 2134 2135
      }
    }
  }

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

2173
  static Handle<Object> PopImpl(Handle<JSArray> receiver) {
2174
    return Subclass::RemoveElement(receiver, AT_END);
2175 2176
  }

2177
  static Handle<Object> ShiftImpl(Handle<JSArray> receiver) {
2178
    return Subclass::RemoveElement(receiver, AT_START);
cbruni's avatar
cbruni committed
2179 2180
  }

2181
  static uint32_t PushImpl(Handle<JSArray> receiver,
2182
                           Arguments* args, uint32_t push_size) {
2183
    Handle<FixedArrayBase> backing_store(receiver->elements());
2184 2185
    return Subclass::AddArguments(receiver, backing_store, args, push_size,
                                  AT_END);
2186
  }
2187

2188 2189
  static uint32_t UnshiftImpl(Handle<JSArray> receiver,
                              Arguments* args, uint32_t unshift_size) {
2190
    Handle<FixedArrayBase> backing_store(receiver->elements());
2191 2192
    return Subclass::AddArguments(receiver, backing_store, args, unshift_size,
                                  AT_START);
2193 2194
  }

2195 2196
  static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
                                    uint32_t end) {
2197
    Isolate* isolate = receiver->GetIsolate();
2198 2199
    Handle<FixedArrayBase> backing_store(receiver->elements(), isolate);
    int result_len = end < start ? 0u : end - start;
2200 2201 2202
    Handle<JSArray> result_array = isolate->factory()->NewJSArray(
        KindTraits::Kind, result_len, result_len);
    DisallowHeapAllocation no_gc;
2203 2204 2205 2206
    Subclass::CopyElementsImpl(*backing_store, start, result_array->elements(),
                               KindTraits::Kind, 0, kPackedSizeNotKnown,
                               result_len);
    Subclass::TryTransitionResultArrayToPacked(result_array);
2207 2208 2209
    return result_array;
  }

2210 2211
  static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver,
                                    uint32_t start, uint32_t delete_count,
2212
                                    Arguments* args, uint32_t add_count) {
2213 2214
    Isolate* isolate = receiver->GetIsolate();
    Heap* heap = isolate->heap();
jgruber's avatar
jgruber committed
2215
    uint32_t length = Smi::ToInt(receiver->length());
cbruni's avatar
cbruni committed
2216
    uint32_t new_length = length - delete_count + add_count;
2217

2218 2219
    ElementsKind kind = KindTraits::Kind;
    if (new_length <= static_cast<uint32_t>(receiver->elements()->length()) &&
2220
        IsSmiOrObjectElementsKind(kind)) {
2221 2222 2223 2224 2225 2226
      HandleScope scope(isolate);
      JSObject::EnsureWritableFastElements(receiver);
    }

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

2227 2228
    if (new_length == 0) {
      receiver->set_elements(heap->empty_fixed_array());
2229
      receiver->set_length(Smi::kZero);
2230 2231 2232 2233
      return isolate->factory()->NewJSArrayWithElements(
          backing_store, KindTraits::Kind, delete_count);
    }

cbruni's avatar
cbruni committed
2234
    // Construct the result array which holds the deleted elements.
2235 2236 2237 2238
    Handle<JSArray> deleted_elements = isolate->factory()->NewJSArray(
        KindTraits::Kind, delete_count, delete_count);
    if (delete_count > 0) {
      DisallowHeapAllocation no_gc;
2239 2240 2241
      Subclass::CopyElementsImpl(*backing_store, start,
                                 deleted_elements->elements(), KindTraits::Kind,
                                 0, kPackedSizeNotKnown, delete_count);
2242 2243
    }

cbruni's avatar
cbruni committed
2244
    // Delete and move elements to make space for add_count new elements.
2245
    if (add_count < delete_count) {
2246 2247
      Subclass::SpliceShrinkStep(isolate, receiver, backing_store, start,
                                 delete_count, add_count, length, new_length);
2248
    } else if (add_count > delete_count) {
2249 2250 2251
      backing_store =
          Subclass::SpliceGrowStep(isolate, receiver, backing_store, start,
                                   delete_count, add_count, length, new_length);
2252 2253
    }

cbruni's avatar
cbruni committed
2254
    // Copy over the arguments.
2255
    Subclass::CopyArguments(args, backing_store, add_count, 3, start);
2256 2257

    receiver->set_length(Smi::FromInt(new_length));
2258
    Subclass::TryTransitionResultArrayToPacked(deleted_elements);
2259 2260 2261
    return deleted_elements;
  }

2262 2263 2264 2265
  static Maybe<bool> CollectValuesOrEntriesImpl(
      Isolate* isolate, Handle<JSObject> object,
      Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
      PropertyFilter filter) {
2266 2267
    Handle<BackingStore> elements(BackingStore::cast(object->elements()),
                                  isolate);
2268
    int count = 0;
2269
    uint32_t length = elements->length();
2270
    for (uint32_t index = 0; index < length; ++index) {
2271
      if (!HasEntryImpl(isolate, *elements, index)) continue;
2272
      Handle<Object> value = Subclass::GetImpl(isolate, *elements, index);
2273 2274 2275 2276 2277 2278 2279 2280 2281
      if (get_entries) {
        value = MakeEntryPair(isolate, index, value);
      }
      values_or_entries->set(count++, *value);
    }
    *nof_items = count;
    return Just(true);
  }

2282 2283 2284 2285 2286 2287
  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);
2288 2289
    if (len > JSArray::kMaxCopyElements && dst_index == 0 &&
        heap->CanMoveObjectStart(*dst_elms)) {
2290 2291 2292 2293 2294 2295 2296 2297 2298
      // 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) {
2299
      if (IsDoubleElementsKind(KindTraits::Kind)) {
2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312
        MemMove(dst_elms->data_start() + dst_index,
                dst_elms->data_start() + src_index, len * kDoubleSize);
      } else {
        DisallowHeapAllocation no_gc;
        heap->MoveElements(FixedArray::cast(*dst_elms), dst_index, src_index,
                           len);
      }
    }
    if (hole_start != hole_end) {
      dst_elms->FillWithHoles(hole_start, hole_end);
    }
  }

2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335
  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) {
2336 2337
        // Only PACKED_ELEMENTS, HOLEY_ELEMENTS, HOLEY_SMI_ELEMENTS, and
        // HOLEY_DOUBLE_ELEMENTS can have `undefined` as a value.
2338 2339
        if (!IsObjectElementsKind(Subclass::kind()) &&
            !IsHoleyElementsKind(Subclass::kind())) {
2340 2341 2342
          return Just(false);
        }

2343 2344
        // Search for `undefined` or The Hole in PACKED_ELEMENTS,
        // HOLEY_ELEMENTS or HOLEY_SMI_ELEMENTS
2345
        if (IsSmiOrObjectElementsKind(Subclass::kind())) {
2346 2347 2348 2349 2350
          auto elements = FixedArray::cast(receiver->elements());

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

2351
            if (IsHoleyElementsKind(Subclass::kind()) &&
2352 2353 2354
                element_k == the_hole) {
              return Just(true);
            }
2355
            if (IsObjectElementsKind(Subclass::kind()) &&
2356 2357 2358 2359 2360 2361
                element_k == undefined) {
              return Just(true);
            }
          }
          return Just(false);
        } else {
2362
          // Search for The Hole in HOLEY_DOUBLE_ELEMENTS
2363
          DCHECK_EQ(Subclass::kind(), HOLEY_DOUBLE_ELEMENTS);
2364 2365 2366
          auto elements = FixedDoubleArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
2367
            if (IsHoleyElementsKind(Subclass::kind()) &&
2368 2369 2370 2371 2372 2373
                elements->is_the_hole(k)) {
              return Just(true);
            }
          }
          return Just(false);
        }
2374
      } else if (!IsObjectElementsKind(Subclass::kind())) {
2375
        // Search for non-number, non-Undefined value, with either
2376 2377
        // PACKED_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, HOLEY_SMI_ELEMENTS or
        // HOLEY_DOUBLE_ELEMENTS. Guaranteed to return false, since these
2378 2379 2380 2381
        // elements kinds can only contain Number values or undefined.
        return Just(false);
      } else {
        // Search for non-number, non-Undefined value with either
2382
        // PACKED_ELEMENTS or HOLEY_ELEMENTS.
2383
        DCHECK(IsObjectElementsKind(Subclass::kind()));
2384 2385 2386 2387
        auto elements = FixedArray::cast(receiver->elements());

        for (uint32_t k = start_from; k < length; ++k) {
          Object* element_k = elements->get(k);
2388
          if (IsHoleyElementsKind(Subclass::kind()) && element_k == the_hole) {
2389 2390 2391 2392 2393 2394 2395 2396 2397 2398
            continue;
          }

          if (value->SameValueZero(element_k)) return Just(true);
        }
        return Just(false);
      }
    } else {
      if (!value->IsNaN()) {
        double search_value = value->Number();
2399
        if (IsDoubleElementsKind(Subclass::kind())) {
2400 2401
          // Search for non-NaN Number in PACKED_DOUBLE_ELEMENTS or
          // HOLEY_DOUBLE_ELEMENTS --- Skip TheHole, and trust UCOMISD or
2402 2403 2404 2405
          // similar operation for result.
          auto elements = FixedDoubleArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
2406
            if (IsHoleyElementsKind(Subclass::kind()) &&
2407 2408 2409 2410 2411 2412 2413
                elements->is_the_hole(k)) {
              continue;
            }
            if (elements->get_scalar(k) == search_value) return Just(true);
          }
          return Just(false);
        } else {
2414 2415
          // Search for non-NaN Number in PACKED_ELEMENTS, HOLEY_ELEMENTS,
          // PACKED_SMI_ELEMENTS or HOLEY_SMI_ELEMENTS --- Skip non-Numbers,
2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428
          // 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
2429
        // abort if ElementsKind is PACKED_SMI_ELEMENTS or HOLEY_SMI_ELEMENTS
2430
        if (IsSmiElementsKind(Subclass::kind())) return Just(false);
2431

2432
        if (IsDoubleElementsKind(Subclass::kind())) {
2433 2434
          // Search for NaN in PACKED_DOUBLE_ELEMENTS or
          // HOLEY_DOUBLE_ELEMENTS --- Skip The Hole and trust
2435 2436 2437 2438
          // std::isnan(elementK) for result
          auto elements = FixedDoubleArray::cast(receiver->elements());

          for (uint32_t k = start_from; k < length; ++k) {
2439
            if (IsHoleyElementsKind(Subclass::kind()) &&
2440 2441 2442 2443 2444 2445 2446
                elements->is_the_hole(k)) {
              continue;
            }
            if (std::isnan(elements->get_scalar(k))) return Just(true);
          }
          return Just(false);
        } else {
2447 2448
          // Search for NaN in PACKED_ELEMENTS, HOLEY_ELEMENTS,
          // PACKED_SMI_ELEMENTS or HOLEY_SMI_ELEMENTS. Return true if
2449
          // elementK->IsHeapNumber() && std::isnan(elementK->Number())
2450
          DCHECK(IsSmiOrObjectElementsKind(Subclass::kind()));
2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461
          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);
        }
      }
    }
  }

2462 2463 2464
  static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
                                                        Handle<JSObject> object,
                                                        uint32_t length) {
2465
    Handle<FixedArray> result = isolate->factory()->NewFixedArray(length);
2466
    Handle<FixedArrayBase> elements(object->elements(), isolate);
2467
    for (uint32_t i = 0; i < length; i++) {
2468
      if (!Subclass::HasElementImpl(isolate, *object, i, *elements)) continue;
2469 2470 2471 2472 2473 2474 2475 2476 2477 2478
      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;
  }

2479
 private:
2480 2481 2482
  // SpliceShrinkStep might modify the backing_store.
  static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver,
                               Handle<FixedArrayBase> backing_store,
cbruni's avatar
cbruni committed
2483 2484 2485
                               uint32_t start, uint32_t delete_count,
                               uint32_t add_count, uint32_t len,
                               uint32_t new_length) {
2486 2487
    const int move_left_count = len - delete_count - start;
    const int move_left_dst_index = start + add_count;
2488 2489 2490
    Subclass::MoveElements(isolate, receiver, backing_store,
                           move_left_dst_index, start + delete_count,
                           move_left_count, new_length, len);
cbruni's avatar
cbruni committed
2491 2492
  }

2493
  // SpliceGrowStep might modify the backing_store.
cbruni's avatar
cbruni committed
2494
  static Handle<FixedArrayBase> SpliceGrowStep(
2495 2496 2497 2498
      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
2499 2500 2501 2502
    // 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())) {
2503 2504 2505
      Subclass::MoveElements(isolate, receiver, backing_store,
                             start + add_count, start + delete_count,
                             (length - delete_count - start), 0, 0);
2506
      // MoveElements updates the backing_store in-place.
cbruni's avatar
cbruni committed
2507
      return backing_store;
2508
    }
cbruni's avatar
cbruni committed
2509 2510 2511
    // New backing storage is needed.
    int capacity = JSObject::NewElementsCapacity(new_length);
    // Partially copy all elements up to start.
2512 2513
    Handle<FixedArrayBase> new_elms = Subclass::ConvertElementsWithCapacity(
        receiver, backing_store, KindTraits::Kind, capacity, start);
cbruni's avatar
cbruni committed
2514
    // Copy the trailing elements after start + delete_count
2515 2516 2517 2518
    Subclass::CopyElementsImpl(*backing_store, start + delete_count, *new_elms,
                               KindTraits::Kind, start + add_count,
                               kPackedSizeNotKnown,
                               ElementsAccessor::kCopyToEndAndInitializeToHole);
cbruni's avatar
cbruni committed
2519 2520
    receiver->set_elements(*new_elms);
    return new_elms;
2521 2522
  }

cbruni's avatar
cbruni committed
2523 2524
  static Handle<Object> RemoveElement(Handle<JSArray> receiver,
                                      Where remove_position) {
2525
    Isolate* isolate = receiver->GetIsolate();
2526
    ElementsKind kind = KindTraits::Kind;
2527
    if (IsSmiOrObjectElementsKind(kind)) {
2528 2529 2530 2531
      HandleScope scope(isolate);
      JSObject::EnsureWritableFastElements(receiver);
    }
    Handle<FixedArrayBase> backing_store(receiver->elements(), isolate);
jgruber's avatar
jgruber committed
2532
    uint32_t length = static_cast<uint32_t>(Smi::ToInt(receiver->length()));
2533
    DCHECK_GT(length, 0);
cbruni's avatar
cbruni committed
2534 2535
    int new_length = length - 1;
    int remove_index = remove_position == AT_START ? 0 : new_length;
2536 2537
    Handle<Object> result =
        Subclass::GetImpl(isolate, *backing_store, remove_index);
cbruni's avatar
cbruni committed
2538
    if (remove_position == AT_START) {
2539 2540
      Subclass::MoveElements(isolate, receiver, backing_store, 0, 1, new_length,
                             0, 0);
cbruni's avatar
cbruni committed
2541
    }
2542
    Subclass::SetLengthImpl(isolate, receiver, new_length, backing_store);
2543

2544
    if (IsHoleyOrDictionaryElementsKind(kind) && result->IsTheHole(isolate)) {
2545
      return isolate->factory()->undefined_value();
cbruni's avatar
cbruni committed
2546 2547 2548 2549 2550 2551 2552
    }
    return result;
  }

  static uint32_t AddArguments(Handle<JSArray> receiver,
                               Handle<FixedArrayBase> backing_store,
                               Arguments* args, uint32_t add_size,
2553
                               Where add_position) {
jgruber's avatar
jgruber committed
2554
    uint32_t length = Smi::ToInt(receiver->length());
2555
    DCHECK_LT(0, add_size);
cbruni's avatar
cbruni committed
2556 2557 2558 2559 2560 2561
    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) {
2562
      // New backing storage is needed.
cbruni's avatar
cbruni committed
2563 2564
      uint32_t capacity = JSObject::NewElementsCapacity(new_length);
      // If we add arguments to the start we have to shift the existing objects.
2565
      int copy_dst_index = add_position == AT_START ? add_size : 0;
cbruni's avatar
cbruni committed
2566
      // Copy over all objects to a new backing_store.
2567
      backing_store = Subclass::ConvertElementsWithCapacity(
cbruni's avatar
cbruni committed
2568 2569 2570
          receiver, backing_store, KindTraits::Kind, capacity, 0,
          copy_dst_index, ElementsAccessor::kCopyToEndAndInitializeToHole);
      receiver->set_elements(*backing_store);
2571
    } else if (add_position == AT_START) {
cbruni's avatar
cbruni committed
2572 2573 2574
      // 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();
2575 2576
      Subclass::MoveElements(isolate, receiver, backing_store, add_size, 0,
                             length, 0, 0);
cbruni's avatar
cbruni committed
2577
    }
2578

2579
    int insertion_index = add_position == AT_START ? 0 : length;
cbruni's avatar
cbruni committed
2580
    // Copy the arguments to the start.
2581
    Subclass::CopyArguments(args, backing_store, add_size, 1, insertion_index);
cbruni's avatar
cbruni committed
2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594
    // 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++) {
2595
      Object* argument = (*args)[src_index + i];
2596
      DCHECK(!argument->IsTheHole(raw_backing_store->GetIsolate()));
2597
      Subclass::SetImpl(raw_backing_store, dst_index + i, argument, mode);
2598 2599
    }
  }
2600 2601
};

2602
template <typename Subclass, typename KindTraits>
2603
class FastSmiOrObjectElementsAccessor
2604
    : public FastElementsAccessor<Subclass, KindTraits> {
2605 2606
 public:
  explicit FastSmiOrObjectElementsAccessor(const char* name)
2607
      : FastElementsAccessor<Subclass, KindTraits>(name) {}
2608

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

2614 2615 2616 2617 2618 2619 2620 2621 2622 2623
  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);
  }

2624
  static Object* GetRaw(FixedArray* backing_store, uint32_t entry) {
2625
    uint32_t index = Subclass::GetIndexForEntryImpl(backing_store, entry);
2626 2627 2628
    return backing_store->get(index);
  }

2629 2630 2631 2632 2633 2634 2635 2636
  // 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.
  static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
                               FixedArrayBase* to, ElementsKind from_kind,
                               uint32_t to_start, int packed_size,
2637
                               int copy_size) {
2638
    DisallowHeapAllocation no_gc;
2639 2640
    ElementsKind to_kind = KindTraits::Kind;
    switch (from_kind) {
2641 2642 2643 2644
      case PACKED_SMI_ELEMENTS:
      case HOLEY_SMI_ELEMENTS:
      case PACKED_ELEMENTS:
      case HOLEY_ELEMENTS:
2645
        CopyObjectToObjectElements(from, from_kind, from_start, to, to_kind,
2646
                                   to_start, copy_size);
2647
        break;
2648 2649
      case PACKED_DOUBLE_ELEMENTS:
      case HOLEY_DOUBLE_ELEMENTS: {
2650
        AllowHeapAllocation allow_allocation;
2651
        DCHECK(IsObjectElementsKind(to_kind));
2652
        CopyDoubleToObjectElements(from, from_start, to, to_start, copy_size);
2653
        break;
2654
      }
2655
      case DICTIONARY_ELEMENTS:
2656 2657
        CopyDictionaryToObjectElements(from, from_start, to, to_kind, to_start,
                                       copy_size);
2658
        break;
2659 2660
      case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
      case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
2661 2662
      case FAST_STRING_WRAPPER_ELEMENTS:
      case SLOW_STRING_WRAPPER_ELEMENTS:
2663
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
2664 2665
      TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
2666 2667 2668 2669 2670 2671
      // This function is currently only used for JSArrays with non-zero
      // length.
      UNREACHABLE();
      break;
      case NO_ELEMENTS:
        break;  // Nothing to do.
2672 2673
    }
  }
2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688

  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.
2689
    if (!value->IsNumber() && !IsObjectElementsKind(Subclass::kind())) {
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700
      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);
  }
2701
};
2702

2703 2704
class FastPackedSmiElementsAccessor
    : public FastSmiOrObjectElementsAccessor<
2705 2706
          FastPackedSmiElementsAccessor,
          ElementsKindTraits<PACKED_SMI_ELEMENTS>> {
2707 2708 2709
 public:
  explicit FastPackedSmiElementsAccessor(const char* name)
      : FastSmiOrObjectElementsAccessor<
2710 2711
            FastPackedSmiElementsAccessor,
            ElementsKindTraits<PACKED_SMI_ELEMENTS>>(name) {}
2712 2713 2714 2715
};

class FastHoleySmiElementsAccessor
    : public FastSmiOrObjectElementsAccessor<
2716 2717
          FastHoleySmiElementsAccessor,
          ElementsKindTraits<HOLEY_SMI_ELEMENTS>> {
2718 2719
 public:
  explicit FastHoleySmiElementsAccessor(const char* name)
2720 2721 2722
      : FastSmiOrObjectElementsAccessor<FastHoleySmiElementsAccessor,
                                        ElementsKindTraits<HOLEY_SMI_ELEMENTS>>(
            name) {}
2723 2724
};

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

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

2746
template <typename Subclass, typename KindTraits>
2747
class FastDoubleElementsAccessor
2748
    : public FastElementsAccessor<Subclass, KindTraits> {
2749 2750
 public:
  explicit FastDoubleElementsAccessor(const char* name)
2751
      : FastElementsAccessor<Subclass, KindTraits>(name) {}
2752

2753 2754
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* backing_store,
                                uint32_t entry) {
2755 2756 2757 2758 2759 2760 2761 2762 2763
    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);
  }

2764 2765 2766 2767 2768 2769 2770 2771 2772 2773
  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());
  }

2774 2775 2776
  static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
                               FixedArrayBase* to, ElementsKind from_kind,
                               uint32_t to_start, int packed_size,
2777
                               int copy_size) {
2778
    DisallowHeapAllocation no_allocation;
2779
    switch (from_kind) {
2780
      case PACKED_SMI_ELEMENTS:
2781
        CopyPackedSmiToDoubleElements(from, from_start, to, to_start,
2782
                                      packed_size, copy_size);
2783
        break;
2784
      case HOLEY_SMI_ELEMENTS:
2785
        CopySmiToDoubleElements(from, from_start, to, to_start, copy_size);
2786
        break;
2787 2788
      case PACKED_DOUBLE_ELEMENTS:
      case HOLEY_DOUBLE_ELEMENTS:
2789
        CopyDoubleToDoubleElements(from, from_start, to, to_start, copy_size);
2790
        break;
2791 2792
      case PACKED_ELEMENTS:
      case HOLEY_ELEMENTS:
2793
        CopyObjectToDoubleElements(from, from_start, to, to_start, copy_size);
2794 2795
        break;
      case DICTIONARY_ELEMENTS:
2796
        CopyDictionaryToDoubleElements(from, from_start, to, to_start,
2797
                                       copy_size);
2798
        break;
2799 2800
      case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
      case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
2801 2802 2803 2804
      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:
2805 2806
      TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
2807 2808 2809 2810
      // This function is currently only used for JSArrays with non-zero
      // length.
      UNREACHABLE();
      break;
2811 2812
    }
  }
2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824

  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);

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

2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845
    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);
  }
2846
};
2847

2848 2849
class FastPackedDoubleElementsAccessor
    : public FastDoubleElementsAccessor<
2850 2851
          FastPackedDoubleElementsAccessor,
          ElementsKindTraits<PACKED_DOUBLE_ELEMENTS>> {
2852 2853
 public:
  explicit FastPackedDoubleElementsAccessor(const char* name)
2854 2855 2856
      : FastDoubleElementsAccessor<FastPackedDoubleElementsAccessor,
                                   ElementsKindTraits<PACKED_DOUBLE_ELEMENTS>>(
            name) {}
2857 2858 2859 2860
};

class FastHoleyDoubleElementsAccessor
    : public FastDoubleElementsAccessor<
2861 2862
          FastHoleyDoubleElementsAccessor,
          ElementsKindTraits<HOLEY_DOUBLE_ELEMENTS>> {
2863 2864
 public:
  explicit FastHoleyDoubleElementsAccessor(const char* name)
2865 2866 2867
      : FastDoubleElementsAccessor<FastHoleyDoubleElementsAccessor,
                                   ElementsKindTraits<HOLEY_DOUBLE_ELEMENTS>>(
            name) {}
2868 2869 2870 2871
};


// Super class for all external element arrays.
2872
template <ElementsKind Kind, typename ctype>
2873
class TypedElementsAccessor
2874 2875
    : public ElementsAccessorBase<TypedElementsAccessor<Kind, ctype>,
                                  ElementsKindTraits<Kind>> {
2876
 public:
2877
  explicit TypedElementsAccessor(const char* name)
2878
      : ElementsAccessorBase<AccessorClass,
2879
                             ElementsKindTraits<Kind> >(name) {}
2880

2881
  typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore;
2882
  typedef TypedElementsAccessor<Kind, ctype> AccessorClass;
2883

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

2889 2890 2891 2892 2893 2894 2895 2896 2897 2898
  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);
  }

2899 2900
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* backing_store,
                                uint32_t entry) {
2901 2902 2903 2904
    return BackingStore::get(BackingStore::cast(backing_store), entry);
  }

  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
2905
    return PropertyDetails(kData, DONT_DELETE, PropertyCellType::kNoCell);
2906
  }
2907

2908
  static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
2909
                                        uint32_t entry) {
2910
    return PropertyDetails(kData, DONT_DELETE, PropertyCellType::kNoCell);
2911 2912
  }

2913 2914
  static bool HasElementImpl(Isolate* isolate, JSObject* holder, uint32_t index,
                             FixedArrayBase* backing_store,
2915
                             PropertyFilter filter) {
2916
    return index < AccessorClass::GetCapacityImpl(holder, backing_store);
2917 2918
  }

2919 2920 2921 2922 2923
  static bool HasAccessorsImpl(JSObject* holder,
                               FixedArrayBase* backing_store) {
    return false;
  }

2924 2925
  static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
                            uint32_t length,
2926
                            Handle<FixedArrayBase> backing_store) {
2927 2928 2929 2930
    // External arrays do not support changing their length.
    UNREACHABLE();
  }

2931
  static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
2932
    UNREACHABLE();
2933
  }
2934

2935 2936 2937 2938 2939
  static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store,
                                       uint32_t entry) {
    return entry;
  }

2940
  static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder,
2941
                                       FixedArrayBase* backing_store,
2942
                                       uint32_t index, PropertyFilter filter) {
2943 2944
    return index < AccessorClass::GetCapacityImpl(holder, backing_store)
               ? index
2945
               : kMaxUInt32;
2946
  }
2947

2948 2949 2950 2951 2952
  static bool WasNeutered(JSObject* holder) {
    JSArrayBufferView* view = JSArrayBufferView::cast(holder);
    return view->WasNeutered();
  }

2953 2954
  static uint32_t GetCapacityImpl(JSObject* holder,
                                  FixedArrayBase* backing_store) {
2955
    if (WasNeutered(holder)) return 0;
2956 2957
    return backing_store->length();
  }
2958

2959 2960 2961 2962 2963
  static uint32_t NumberOfElementsImpl(JSObject* receiver,
                                       FixedArrayBase* backing_store) {
    return AccessorClass::GetCapacityImpl(receiver, backing_store);
  }

2964 2965 2966
  static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
                                              KeyAccumulator* accumulator,
                                              AddKeyConversion convert) {
2967
    Isolate* isolate = receiver->GetIsolate();
2968
    Handle<FixedArrayBase> elements(receiver->elements());
2969 2970
    uint32_t length = AccessorClass::GetCapacityImpl(*receiver, *elements);
    for (uint32_t i = 0; i < length; i++) {
2971
      Handle<Object> value = AccessorClass::GetImpl(isolate, *elements, i);
2972 2973 2974
      accumulator->AddKey(value, convert);
    }
  }
2975 2976 2977 2978 2979 2980 2981 2982 2983 2984

  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) {
      Handle<FixedArrayBase> elements(object->elements());
      uint32_t length = AccessorClass::GetCapacityImpl(*object, *elements);
      for (uint32_t index = 0; index < length; ++index) {
2985 2986
        Handle<Object> value =
            AccessorClass::GetImpl(isolate, *elements, index);
2987 2988 2989 2990 2991 2992 2993 2994 2995
        if (get_entries) {
          value = MakeEntryPair(isolate, index, value);
        }
        values_or_entries->set(count++, *value);
      }
    }
    *nof_items = count;
    return Just(true);
  }
2996

2997 2998 2999 3000 3001
  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());
3002
    DCHECK(obj_value->IsNumeric());
3003

3004
    ctype value = BackingStore::FromHandle(obj_value);
3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017

    // 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;
  }

3018 3019 3020 3021 3022
  static Maybe<bool> IncludesValueImpl(Isolate* isolate,
                                       Handle<JSObject> receiver,
                                       Handle<Object> value,
                                       uint32_t start_from, uint32_t length) {
    DisallowHeapAllocation no_gc;
3023

3024 3025 3026 3027 3028 3029
    // TODO(caitp): return Just(false) here when implementing strict throwing on
    // neutered views.
    if (WasNeutered(*receiver)) {
      return Just(value->IsUndefined(isolate) && length > start_from);
    }

3030 3031 3032 3033 3034
    BackingStore* elements = BackingStore::cast(receiver->elements());
    if (value->IsUndefined(isolate) &&
        length > static_cast<uint32_t>(elements->length())) {
      return Just(true);
    }
3035
    ctype typed_search_value;
3036 3037 3038 3039 3040 3041
    // 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();
    }

3042 3043 3044 3045 3046
    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);
3047
    } else {
3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069
      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.
3070 3071
      }
    }
3072 3073 3074 3075 3076 3077

    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);
3078
  }
3079 3080 3081 3082 3083 3084 3085

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

3086 3087
    if (WasNeutered(*receiver)) return Just<int64_t>(-1);

3088
    BackingStore* elements = BackingStore::cast(receiver->elements());
3089
    ctype typed_search_value;
3090

3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109
    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.
3110 3111
        return Just<int64_t>(-1);
      }
3112 3113 3114 3115
      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.
      }
3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129
    }

    // 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);
  }
3130 3131 3132 3133 3134 3135 3136 3137 3138

  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());
3139
    ctype typed_search_value;
3140

3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159
    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.
3160 3161
        return Just<int64_t>(-1);
      }
3162 3163 3164 3165
      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.
      }
3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176
    }

    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);
  }
3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189

  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);
  }
3190

3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204
  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);
    Handle<BackingStore> elements(BackingStore::cast(object->elements()));
    for (uint32_t i = 0; i < length; i++) {
      Handle<Object> value = AccessorClass::GetImpl(isolate, *elements, i);
      result->set(i, *value);
    }
    return result;
  }

3205 3206 3207 3208 3209 3210 3211
  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());
3212
    DCHECK_LE(start, end);
3213
    DCHECK_LE(end, source->length_value());
3214

3215 3216
    size_t count = end - start;
    DCHECK_LE(count, destination->length_value());
3217

3218 3219 3220
    FixedTypedArrayBase* src_elements =
        FixedTypedArrayBase::cast(source->elements());
    BackingStore* dest_elements = BackingStore::cast(destination->elements());
3221

3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234
    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++;
3235
      }
3236
      return;
3237 3238
    }

3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249
    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;
3250 3251
    }
  }
3252 3253 3254 3255 3256 3257 3258 3259

  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>
3260
  static void CopyBetweenBackingStores(void* source_data_ptr,
3261 3262
                                       BackingStore* dest, size_t length,
                                       uint32_t offset) {
3263
    DisallowHeapAllocation no_gc;
3264
    for (uint32_t i = 0; i < length; i++) {
3265 3266 3267 3268 3269
      // 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);
3270
      dest->set(offset + i, dest->from(elem));
3271 3272 3273
    }
  }

3274 3275 3276
  static void CopyElementsFromTypedArray(JSTypedArray* source,
                                         JSTypedArray* destination,
                                         size_t length, uint32_t offset) {
3277
    // The source is a typed array, so we know we don't need to do ToNumber
3278
    // side-effects, as the source elements will always be a number.
3279 3280
    DisallowHeapAllocation no_gc;

3281 3282 3283 3284
    FixedTypedArrayBase* source_elements =
        FixedTypedArrayBase::cast(source->elements());
    BackingStore* destination_elements =
        BackingStore::cast(destination->elements());
3285

3286
    DCHECK_LE(offset, destination->length_value());
3287
    DCHECK_LE(length, destination->length_value() - offset);
3288
    DCHECK(source->length()->IsSmi());
3289
    DCHECK_LE(length, source->length_value());
3290 3291 3292 3293 3294 3295

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

    bool same_type = source_type == destination_type;
3296
    bool same_size = source->element_size() == destination->element_size();
3297 3298 3299
    bool both_are_simple = HasSimpleRepresentation(source_type) &&
                           HasSimpleRepresentation(destination_type);

3300 3301
    uint8_t* source_data = static_cast<uint8_t*>(source_elements->DataPtr());
    uint8_t* dest_data = static_cast<uint8_t*>(destination_elements->DataPtr());
3302 3303
    size_t source_byte_length = NumberToSize(source->byte_length());
    size_t dest_byte_length = NumberToSize(destination->byte_length());
3304

3305 3306 3307 3308 3309
    // 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)) {
3310
      size_t element_size = source->element_size();
3311 3312
      std::memmove(dest_data + offset * element_size, source_data,
                   length * element_size);
3313
    } else {
3314
      std::unique_ptr<uint8_t[]> cloned_source_elements;
3315 3316 3317 3318

      // If the typedarrays are overlapped, clone the source.
      if (dest_data + dest_byte_length > source_data &&
          source_data + source_byte_length > dest_data) {
3319 3320 3321 3322
        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();
3323 3324
      }

3325
      switch (source->GetElementsKind()) {
3326 3327 3328 3329
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size)     \
  case TYPE##_ELEMENTS:                                     \
    CopyBetweenBackingStores<Type##ArrayTraits>(            \
        source_data, destination_elements, length, offset); \
3330 3331 3332 3333 3334 3335 3336 3337 3338 3339
    break;
        TYPED_ARRAYS(TYPED_ARRAY_CASE)
        default:
          UNREACHABLE();
          break;
      }
#undef TYPED_ARRAY_CASE
    }
  }

3340 3341 3342 3343 3344
  static bool HoleyPrototypeLookupRequired(Isolate* isolate, Context* context,
                                           JSArray* source) {
    DisallowHeapAllocation no_gc;
    DisallowJavascriptExecution no_js(isolate);

3345
#ifdef V8_ENABLE_FORCE_SLOW_PATH
3346 3347 3348
    if (isolate->force_slow_path()) return true;
#endif

3349
    Object* source_proto = source->map()->prototype();
3350

3351 3352 3353 3354
    // 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;
3355 3356
    if (!context->native_context()->is_initial_array_prototype(
            JSObject::cast(source_proto))) {
3357 3358
      return true;
    }
3359 3360

    return !isolate->IsNoElementsProtectorIntact(context);
3361 3362
  }

3363 3364 3365
  static bool TryCopyElementsFastNumber(Context* context, JSArray* source,
                                        JSTypedArray* destination,
                                        size_t length, uint32_t offset) {
3366
    if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) return false;
3367
    Isolate* isolate = source->GetIsolate();
3368
    DisallowHeapAllocation no_gc;
3369 3370
    DisallowJavascriptExecution no_js(isolate);

3371 3372 3373
    ElementsKind kind = source->GetElementsKind();
    BackingStore* dest = BackingStore::cast(destination->elements());

3374 3375 3376 3377 3378
    // 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.
3379
    if (HoleyPrototypeLookupRequired(isolate, context, source)) return false;
3380 3381 3382 3383

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

    // Fastpath for packed Smi kind.
3384
    if (kind == PACKED_SMI_ELEMENTS) {
3385 3386 3387 3388 3389
      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
3390
        int int_value = Smi::ToInt(elem);
3391
        dest->set(offset + i, dest->from(int_value));
3392 3393
      }
      return true;
3394
    } else if (kind == HOLEY_SMI_ELEMENTS) {
3395 3396 3397
      FixedArray* source_store = FixedArray::cast(source->elements());
      for (uint32_t i = 0; i < length; i++) {
        if (source_store->is_the_hole(isolate, i)) {
3398
          dest->SetValue(offset + i, undefined);
3399 3400 3401
        } else {
          Object* elem = source_store->get(i);
          DCHECK(elem->IsSmi());
jgruber's avatar
jgruber committed
3402
          int int_value = Smi::ToInt(elem);
3403
          dest->set(offset + i, dest->from(int_value));
3404 3405 3406
        }
      }
      return true;
3407
    } else if (kind == PACKED_DOUBLE_ELEMENTS) {
3408 3409 3410 3411 3412 3413 3414 3415 3416
      // 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);
3417
        dest->set(offset + i, dest->from(elem));
3418 3419
      }
      return true;
3420
    } else if (kind == HOLEY_DOUBLE_ELEMENTS) {
3421 3422 3423 3424
      FixedDoubleArray* source_store =
          FixedDoubleArray::cast(source->elements());
      for (uint32_t i = 0; i < length; i++) {
        if (source_store->is_the_hole(i)) {
3425
          dest->SetValue(offset + i, undefined);
3426 3427
        } else {
          double elem = source_store->get_scalar(i);
3428
          dest->set(offset + i, dest->from(elem));
3429 3430 3431
        }
      }
      return true;
3432 3433 3434 3435
    }
    return false;
  }

3436
  static Object* CopyElementsHandleSlow(Handle<Object> source,
3437
                                        Handle<JSTypedArray> destination,
3438
                                        size_t length, uint32_t offset) {
3439
    Isolate* isolate = destination->GetIsolate();
3440 3441 3442
    Handle<BackingStore> destination_elements(
        BackingStore::cast(destination->elements()));
    for (uint32_t i = 0; i < length; i++) {
3443
      LookupIterator it(isolate, source, i);
3444 3445 3446
      Handle<Object> elem;
      ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
                                         Object::GetProperty(&it));
3447 3448 3449 3450 3451 3452 3453
      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));
      }
3454 3455 3456 3457 3458 3459 3460 3461 3462 3463

      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));
      }
3464 3465
      // The spec says we store the length, then get each element, so we don't
      // need to check changes to length.
3466
      destination_elements->SetValue(offset + i, *elem);
3467
    }
3468
    return *isolate->factory()->undefined_value();
3469 3470
  }

3471 3472 3473
  // 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.
3474
  static Object* CopyElementsHandleImpl(Handle<Object> source,
3475
                                        Handle<JSObject> destination,
3476 3477
                                        size_t length, uint32_t offset) {
    Isolate* isolate = destination->GetIsolate();
3478 3479
    Handle<JSTypedArray> destination_ta =
        Handle<JSTypedArray>::cast(destination);
3480
    DCHECK_LE(offset + length, destination_ta->length_value());
3481

3482 3483
    if (length == 0) return *isolate->factory()->undefined_value();

3484 3485 3486
    // All conversions from TypedArrays can be done without allocation.
    if (source->IsJSTypedArray()) {
      Handle<JSTypedArray> source_ta = Handle<JSTypedArray>::cast(source);
3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504
      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));
        }
      }
3505 3506 3507 3508 3509 3510
      // 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()) {
        CopyElementsFromTypedArray(*source_ta, *destination_ta, length, offset);
        return *isolate->factory()->undefined_value();
      }
3511 3512 3513 3514 3515
    }

    // Fast cases for packed numbers kinds where we don't need to allocate.
    if (source->IsJSArray()) {
      Handle<JSArray> source_array = Handle<JSArray>::cast(source);
3516 3517
      if (TryCopyElementsFastNumber(isolate->context(), *source_array,
                                    *destination_ta, length, offset)) {
3518
        return *isolate->factory()->undefined_value();
3519 3520 3521 3522
      }
    }
    // Final generic case that handles prototype chain lookups, getters, proxies
    // and observable side effects via valueOf, etc.
3523
    return CopyElementsHandleSlow(source, destination_ta, length, offset);
3524
  }
3525
};
3526

3527 3528
#define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype, size) \
  typedef TypedElementsAccessor<TYPE##_ELEMENTS, ctype>        \
3529
      Fixed##Type##ElementsAccessor;
3530

3531 3532
TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR)
#undef FIXED_ELEMENTS_ACCESSOR
3533

3534
template <typename Subclass, typename ArgumentsAccessor, typename KindTraits>
3535
class SloppyArgumentsElementsAccessor
3536
    : public ElementsAccessorBase<Subclass, KindTraits> {
3537 3538
 public:
  explicit SloppyArgumentsElementsAccessor(const char* name)
3539
      : ElementsAccessorBase<Subclass, KindTraits>(name) {
3540 3541
    USE(KindTraits::Kind);
  }
3542

3543 3544 3545 3546 3547 3548
  static void ConvertArgumentsStoreResult(
      Isolate* isolate, Handle<SloppyArgumentsElements> elements,
      Handle<Object> result) {
    UNREACHABLE();
  }

3549 3550
  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* parameters,
                                uint32_t entry) {
3551 3552 3553
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(parameters), isolate);
    uint32_t length = elements->parameter_map_length();
3554
    if (entry < length) {
3555
      // Read context mapped entry.
3556
      DisallowHeapAllocation no_gc;
3557 3558 3559
      Object* probe = elements->get_mapped_entry(entry);
      DCHECK(!probe->IsTheHole(isolate));
      Context* context = elements->context();
jgruber's avatar
jgruber committed
3560
      int context_entry = Smi::ToInt(probe);
3561
      DCHECK(!context->get(context_entry)->IsTheHole(isolate));
3562
      return handle(context->get(context_entry), isolate);
3563
    } else {
3564
      // Entry is not context mapped, defer to the arguments.
3565
      Handle<Object> result = ArgumentsAccessor::GetImpl(
3566 3567
          isolate, elements->arguments(), entry - length);
      return Subclass::ConvertArgumentsStoreResult(isolate, elements, result);
3568 3569
    }
  }
3570

3571 3572 3573 3574 3575
  static void TransitionElementsKindImpl(Handle<JSObject> object,
                                         Handle<Map> map) {
    UNREACHABLE();
  }

3576 3577
  static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
                                         uint32_t capacity) {
3578
    UNREACHABLE();
3579 3580
  }

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

3586 3587
  static inline void SetImpl(FixedArrayBase* store, uint32_t entry,
                             Object* value) {
3588 3589
    SloppyArgumentsElements* elements = SloppyArgumentsElements::cast(store);
    uint32_t length = elements->parameter_map_length();
3590
    if (entry < length) {
3591 3592 3593 3594 3595
      // Store context mapped entry.
      DisallowHeapAllocation no_gc;
      Object* probe = elements->get_mapped_entry(entry);
      DCHECK(!probe->IsTheHole(store->GetIsolate()));
      Context* context = elements->context();
jgruber's avatar
jgruber committed
3596
      int context_entry = Smi::ToInt(probe);
3597
      DCHECK(!context->get(context_entry)->IsTheHole(store->GetIsolate()));
3598
      context->set(context_entry, value);
3599
    } else {
3600 3601
      //  Entry is not context mapped defer to arguments.
      FixedArray* arguments = elements->arguments();
3602 3603 3604
      Object* current = ArgumentsAccessor::GetRaw(arguments, entry - length);
      if (current->IsAliasedArgumentsEntry()) {
        AliasedArgumentsEntry* alias = AliasedArgumentsEntry::cast(current);
3605
        Context* context = elements->context();
3606
        int context_entry = alias->aliased_context_slot();
3607
        DCHECK(!context->get(context_entry)->IsTheHole(store->GetIsolate()));
3608 3609 3610 3611
        context->set(context_entry, value);
      } else {
        ArgumentsAccessor::SetImpl(arguments, entry - length, value);
      }
3612 3613 3614
    }
  }

3615 3616
  static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
                            uint32_t length,
3617 3618 3619
                            Handle<FixedArrayBase> parameter_map) {
    // Sloppy arguments objects are not arrays.
    UNREACHABLE();
3620 3621
  }

3622 3623 3624 3625
  static uint32_t GetCapacityImpl(JSObject* holder, FixedArrayBase* store) {
    SloppyArgumentsElements* elements = SloppyArgumentsElements::cast(store);
    FixedArray* arguments = elements->arguments();
    return elements->parameter_map_length() +
3626
           ArgumentsAccessor::GetCapacityImpl(holder, arguments);
3627 3628
  }

3629 3630
  static uint32_t GetMaxNumberOfEntries(JSObject* holder,
                                        FixedArrayBase* backing_store) {
3631 3632 3633 3634
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(backing_store);
    FixedArrayBase* arguments = elements->arguments();
    return elements->parameter_map_length() +
3635 3636 3637
           ArgumentsAccessor::GetMaxNumberOfEntries(holder, arguments);
  }

3638 3639
  static uint32_t NumberOfElementsImpl(JSObject* receiver,
                                       FixedArrayBase* backing_store) {
3640 3641 3642 3643
    Isolate* isolate = receiver->GetIsolate();
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(backing_store);
    FixedArrayBase* arguments = elements->arguments();
3644
    uint32_t nof_elements = 0;
3645
    uint32_t length = elements->parameter_map_length();
3646
    for (uint32_t entry = 0; entry < length; entry++) {
3647
      if (HasParameterMapArg(isolate, elements, entry)) nof_elements++;
3648 3649 3650 3651 3652
    }
    return nof_elements +
           ArgumentsAccessor::NumberOfElementsImpl(receiver, arguments);
  }

3653 3654 3655
  static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
                                              KeyAccumulator* accumulator,
                                              AddKeyConversion convert) {
3656 3657 3658
    Isolate* isolate = accumulator->isolate();
    Handle<FixedArrayBase> elements(receiver->elements(), isolate);
    uint32_t length = GetCapacityImpl(*receiver, *elements);
3659
    for (uint32_t entry = 0; entry < length; entry++) {
3660
      if (!HasEntryImpl(isolate, *elements, entry)) continue;
3661
      Handle<Object> value = GetImpl(isolate, *elements, entry);
3662 3663 3664 3665
      accumulator->AddKey(value, convert);
    }
  }

3666 3667
  static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* parameters,
                           uint32_t entry) {
3668 3669 3670
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(parameters);
    uint32_t length = elements->parameter_map_length();
3671
    if (entry < length) {
3672
      return HasParameterMapArg(isolate, elements, entry);
3673
    }
3674
    FixedArrayBase* arguments = elements->arguments();
3675
    return ArgumentsAccessor::HasEntryImpl(isolate, arguments, entry - length);
3676 3677
  }

3678 3679
  static bool HasAccessorsImpl(JSObject* holder,
                               FixedArrayBase* backing_store) {
3680 3681 3682
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(backing_store);
    FixedArray* arguments = elements->arguments();
3683 3684 3685
    return ArgumentsAccessor::HasAccessorsImpl(holder, arguments);
  }

3686 3687
  static uint32_t GetIndexForEntryImpl(FixedArrayBase* parameters,
                                       uint32_t entry) {
3688 3689 3690
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(parameters);
    uint32_t length = elements->parameter_map_length();
3691
    if (entry < length) return entry;
3692
    FixedArray* arguments = elements->arguments();
3693
    return ArgumentsAccessor::GetIndexForEntryImpl(arguments, entry - length);
3694 3695
  }

3696
  static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder,
3697
                                       FixedArrayBase* parameters,
3698
                                       uint32_t index, PropertyFilter filter) {
3699 3700 3701 3702
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(parameters);
    if (HasParameterMapArg(isolate, elements, index)) return index;
    FixedArray* arguments = elements->arguments();
3703 3704
    uint32_t entry = ArgumentsAccessor::GetEntryForIndexImpl(
        isolate, holder, arguments, index, filter);
3705
    if (entry == kMaxUInt32) return kMaxUInt32;
3706 3707
    // Arguments entries could overlap with the dictionary entries, hence offset
    // them by the number of context mapped entries.
3708
    return elements->parameter_map_length() + entry;
3709 3710
  }

3711
  static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
3712 3713 3714
    SloppyArgumentsElements* elements =
        SloppyArgumentsElements::cast(holder->elements());
    uint32_t length = elements->parameter_map_length();
3715
    if (entry < length) {
3716
      return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
3717
    }
3718
    FixedArray* arguments = elements->arguments();
3719
    return ArgumentsAccessor::GetDetailsImpl(arguments, entry - length);
3720
  }
3721

3722 3723 3724 3725
  static bool HasParameterMapArg(Isolate* isolate,
                                 SloppyArgumentsElements* elements,
                                 uint32_t index) {
    uint32_t length = elements->parameter_map_length();
3726
    if (index >= length) return false;
3727
    return !elements->get_mapped_entry(index)->IsTheHole(isolate);
3728
  }
3729

3730
  static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
3731 3732
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(obj->elements()));
3733
    uint32_t length = elements->parameter_map_length();
3734 3735 3736 3737 3738 3739 3740
    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.
3741
    if (entry < length) {
3742
      elements->set_mapped_entry(entry, obj->GetHeap()->the_hole_value());
3743 3744
    }
  }
3745

3746 3747 3748 3749 3750 3751 3752
  static void SloppyDeleteImpl(Handle<JSObject> obj,
                               Handle<SloppyArgumentsElements> elements,
                               uint32_t entry) {
    // Implemented in subclasses.
    UNREACHABLE();
  }

3753 3754
  static void CollectElementIndicesImpl(Handle<JSObject> object,
                                        Handle<FixedArrayBase> backing_store,
3755
                                        KeyAccumulator* keys) {
3756 3757 3758 3759 3760
    Isolate* isolate = keys->isolate();
    uint32_t nof_indices = 0;
    Handle<FixedArray> indices = isolate->factory()->NewFixedArray(
        GetCapacityImpl(*object, *backing_store));
    DirectCollectElementIndicesImpl(isolate, object, backing_store,
3761 3762
                                    GetKeysConversion::kKeepNumbers,
                                    ENUMERABLE_STRINGS, indices, &nof_indices);
3763 3764 3765
    SortIndices(indices, nof_indices);
    for (uint32_t i = 0; i < nof_indices; i++) {
      keys->AddKey(indices->get(i));
3766 3767 3768 3769 3770 3771 3772 3773
    }
  }

  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) {
3774 3775 3776
    Handle<SloppyArgumentsElements> elements =
        Handle<SloppyArgumentsElements>::cast(backing_store);
    uint32_t length = elements->parameter_map_length();
3777 3778

    for (uint32_t i = 0; i < length; ++i) {
3779
      if (elements->get_mapped_entry(i)->IsTheHole(isolate)) continue;
3780
      if (convert == GetKeysConversion::kConvertToString) {
3781 3782 3783 3784 3785 3786 3787 3788
        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++;
    }

3789
    Handle<FixedArray> store(elements->arguments(), isolate);
3790 3791 3792 3793
    return ArgumentsAccessor::DirectCollectElementIndicesImpl(
        isolate, object, store, convert, filter, list, nof_indices,
        insertion_index);
  }
3794 3795 3796 3797 3798 3799

  static Maybe<bool> IncludesValueImpl(Isolate* isolate,
                                       Handle<JSObject> object,
                                       Handle<Object> value,
                                       uint32_t start_from, uint32_t length) {
    DCHECK(JSObject::PrototypeHasNoElements(isolate, *object));
3800
    Handle<Map> original_map(object->map(), isolate);
3801 3802
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
3803 3804 3805
    bool search_for_hole = value->IsUndefined(isolate);

    for (uint32_t k = start_from; k < length; ++k) {
3806
      DCHECK_EQ(object->map(), *original_map);
3807 3808
      uint32_t entry =
          GetEntryForIndexImpl(isolate, *object, *elements, k, ALL_PROPERTIES);
3809 3810 3811 3812 3813
      if (entry == kMaxUInt32) {
        if (search_for_hole) return Just(true);
        continue;
      }

3814
      Handle<Object> element_k = Subclass::GetImpl(isolate, *elements, entry);
3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835

      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);
  }
3836 3837 3838 3839 3840 3841

  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));
3842
    Handle<Map> original_map(object->map(), isolate);
3843 3844
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
3845 3846

    for (uint32_t k = start_from; k < length; ++k) {
3847
      DCHECK_EQ(object->map(), *original_map);
3848 3849
      uint32_t entry =
          GetEntryForIndexImpl(isolate, *object, *elements, k, ALL_PROPERTIES);
3850 3851 3852 3853
      if (entry == kMaxUInt32) {
        continue;
      }

3854
      Handle<Object> element_k = Subclass::GetImpl(isolate, *elements, entry);
3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877

      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);
  }
3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900

  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;
  }
3901 3902 3903
};


3904 3905 3906 3907 3908 3909 3910 3911 3912 3913
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) {}

3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927
  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;
  }
3928 3929 3930 3931 3932
  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;
3933
    Isolate* isolate = obj->GetIsolate();
3934 3935
    Handle<NumberDictionary> dict(NumberDictionary::cast(elements->arguments()),
                                  isolate);
3936
    int length = elements->parameter_map_length();
3937
    dict = NumberDictionary::DeleteEntry(dict, entry - length);
3938
    elements->set_arguments(*dict);
3939
  }
3940
  static void AddImpl(Handle<JSObject> object, uint32_t index,
3941 3942
                      Handle<Object> value, PropertyAttributes attributes,
                      uint32_t new_capacity) {
3943 3944 3945 3946 3947
    Isolate* isolate = object->GetIsolate();
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
    Handle<FixedArrayBase> old_arguments(
        FixedArrayBase::cast(elements->arguments()), isolate);
3948 3949 3950
    Handle<NumberDictionary> dictionary =
        old_arguments->IsNumberDictionary()
            ? Handle<NumberDictionary>::cast(old_arguments)
3951
            : JSObject::NormalizeElements(object);
3952
    PropertyDetails details(kData, attributes, PropertyCellType::kNoCell);
3953 3954
    Handle<NumberDictionary> new_dictionary =
        NumberDictionary::Add(dictionary, index, value, details);
3955
    if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
3956
    if (*dictionary != *new_dictionary) {
3957
      elements->set_arguments(*new_dictionary);
3958 3959 3960 3961
    }
  }

  static void ReconfigureImpl(Handle<JSObject> object,
3962
                              Handle<FixedArrayBase> store, uint32_t entry,
3963 3964
                              Handle<Object> value,
                              PropertyAttributes attributes) {
3965
    Isolate* isolate = store->GetIsolate();
3966 3967 3968
    Handle<SloppyArgumentsElements> elements =
        Handle<SloppyArgumentsElements>::cast(store);
    uint32_t length = elements->parameter_map_length();
3969
    if (entry < length) {
3970
      Object* probe = elements->get_mapped_entry(entry);
3971
      DCHECK(!probe->IsTheHole(isolate));
3972
      Context* context = elements->context();
jgruber's avatar
jgruber committed
3973
      int context_entry = Smi::ToInt(probe);
3974
      DCHECK(!context->get(context_entry)->IsTheHole(isolate));
3975
      context->set(context_entry, *value);
3976 3977

      // Redefining attributes of an aliased element destroys fast aliasing.
3978
      elements->set_mapped_entry(entry, isolate->heap()->the_hole_value());
3979 3980
      // For elements that are still writable we re-establish slow aliasing.
      if ((attributes & READ_ONLY) == 0) {
3981
        value = isolate->factory()->NewAliasedArgumentsEntry(context_entry);
3982 3983
      }

3984
      PropertyDetails details(kData, attributes, PropertyCellType::kNoCell);
3985 3986 3987
      Handle<NumberDictionary> arguments(
          NumberDictionary::cast(elements->arguments()), isolate);
      arguments = NumberDictionary::Add(arguments, entry, value, details);
3988 3989 3990 3991
      // If the attributes were NONE, we would have called set rather than
      // reconfigure.
      DCHECK_NE(NONE, attributes);
      object->RequireSlowElements(*arguments);
3992
      elements->set_arguments(*arguments);
3993
    } else {
3994
      Handle<FixedArrayBase> arguments(elements->arguments(), isolate);
3995
      DictionaryElementsAccessor::ReconfigureImpl(
3996
          object, arguments, entry - length, value, attributes);
3997 3998 3999 4000 4001
    }
  }
};


4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012
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) {}

4013 4014 4015 4016 4017 4018 4019
  static Handle<Object> ConvertArgumentsStoreResult(
      Isolate* isolate, Handle<SloppyArgumentsElements> paramtere_map,
      Handle<Object> result) {
    DCHECK(!result->IsAliasedArgumentsEntry());
    return result;
  }

4020
  static Handle<FixedArray> GetArguments(Isolate* isolate,
4021 4022 4023
                                         FixedArrayBase* store) {
    SloppyArgumentsElements* elements = SloppyArgumentsElements::cast(store);
    return Handle<FixedArray>(elements->arguments(), isolate);
4024 4025
  }

4026
  static Handle<NumberDictionary> NormalizeImpl(
4027
      Handle<JSObject> object, Handle<FixedArrayBase> elements) {
4028 4029
    Handle<FixedArray> arguments =
        GetArguments(elements->GetIsolate(), *elements);
4030 4031 4032
    return FastHoleyObjectElementsAccessor::NormalizeImpl(object, arguments);
  }

4033
  static Handle<NumberDictionary> NormalizeArgumentsElements(
4034 4035
      Handle<JSObject> object, Handle<SloppyArgumentsElements> elements,
      uint32_t* entry) {
4036
    Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(object);
4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053
    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);
4054 4055
  }

4056
  static void AddImpl(Handle<JSObject> object, uint32_t index,
4057 4058 4059
                      Handle<Object> value, PropertyAttributes attributes,
                      uint32_t new_capacity) {
    DCHECK_EQ(NONE, attributes);
4060 4061 4062 4063
    Isolate* isolate = object->GetIsolate();
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
    Handle<FixedArray> old_arguments(elements->arguments(), isolate);
4064
    if (old_arguments->IsNumberDictionary() ||
4065
        static_cast<uint32_t>(old_arguments->length()) < new_capacity) {
4066 4067
      GrowCapacityAndConvertImpl(object, new_capacity);
    }
4068
    FixedArray* arguments = elements->arguments();
4069 4070 4071 4072 4073 4074
    // 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);
4075
  }
4076

4077
  static void ReconfigureImpl(Handle<JSObject> object,
4078
                              Handle<FixedArrayBase> store, uint32_t entry,
4079 4080
                              Handle<Object> value,
                              PropertyAttributes attributes) {
4081 4082 4083 4084
    DCHECK_EQ(object->elements(), *store);
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(*store));
    NormalizeArgumentsElements(object, elements, &entry);
4085
    SlowSloppyArgumentsElementsAccessor::ReconfigureImpl(object, store, entry,
4086
                                                         value, attributes);
4087
  }
4088

4089 4090 4091 4092 4093 4094
  static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
                               FixedArrayBase* to, ElementsKind from_kind,
                               uint32_t to_start, int packed_size,
                               int copy_size) {
    DCHECK(!to->IsDictionary());
    if (from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS) {
4095
      CopyDictionaryToObjectElements(from, from_start, to, HOLEY_ELEMENTS,
4096 4097 4098
                                     to_start, copy_size);
    } else {
      DCHECK_EQ(FAST_SLOPPY_ARGUMENTS_ELEMENTS, from_kind);
4099 4100
      CopyObjectToObjectElements(from, HOLEY_ELEMENTS, from_start, to,
                                 HOLEY_ELEMENTS, to_start, copy_size);
4101 4102 4103 4104 4105
    }
  }

  static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
                                         uint32_t capacity) {
4106 4107 4108 4109 4110
    Isolate* isolate = object->GetIsolate();
    Handle<SloppyArgumentsElements> elements(
        SloppyArgumentsElements::cast(object->elements()), isolate);
    Handle<FixedArray> old_arguments(FixedArray::cast(elements->arguments()),
                                     isolate);
4111 4112 4113 4114
    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 ||
4115 4116 4117
           static_cast<uint32_t>(old_arguments->length()) < capacity);
    Handle<FixedArrayBase> arguments =
        ConvertElementsWithCapacity(object, old_arguments, from_kind, capacity);
4118 4119 4120
    Handle<Map> new_map = JSObject::GetElementsTransitionMap(
        object, FAST_SLOPPY_ARGUMENTS_ELEMENTS);
    JSObject::MigrateToMap(object, new_map);
4121
    elements->set_arguments(FixedArray::cast(*arguments));
4122
    JSObject::ValidateElements(*object);
4123 4124 4125
  }
};

4126
template <typename Subclass, typename BackingStoreAccessor, typename KindTraits>
4127
class StringWrapperElementsAccessor
4128
    : public ElementsAccessorBase<Subclass, KindTraits> {
4129 4130
 public:
  explicit StringWrapperElementsAccessor(const char* name)
4131
      : ElementsAccessorBase<Subclass, KindTraits>(name) {
4132 4133 4134
    USE(KindTraits::Kind);
  }

4135 4136 4137 4138 4139
  static Handle<Object> GetInternalImpl(Handle<JSObject> holder,
                                        uint32_t entry) {
    return GetImpl(holder, entry);
  }

4140 4141 4142 4143 4144 4145 4146 4147
  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(
          String::Flatten(string)->Get(entry));
    }
4148 4149 4150 4151 4152 4153 4154
    return BackingStoreAccessor::GetImpl(isolate, holder->elements(),
                                         entry - length);
  }

  static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase* elements,
                                uint32_t entry) {
    UNREACHABLE();
4155 4156 4157 4158 4159 4160 4161
  }

  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);
4162
      return PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
4163 4164 4165 4166
    }
    return BackingStoreAccessor::GetDetailsImpl(holder, entry - length);
  }

4167
  static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder,
4168 4169 4170 4171 4172
                                       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(
4173
        isolate, holder, backing_store, index, filter);
4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198
    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()));
4199 4200 4201 4202 4203 4204
    // 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)) {
4205
      GrowCapacityAndConvertImpl(object, new_capacity);
4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241
    }
    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);
    string = String::Flatten(string);
    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,
4242
                                        KeyAccumulator* keys) {
4243
    uint32_t length = GetString(*object)->length();
4244
    Factory* factory = keys->isolate()->factory();
4245
    for (uint32_t i = 0; i < length; i++) {
4246
      keys->AddKey(factory->NewNumberFromUint(i));
4247
    }
4248 4249
    BackingStoreAccessor::CollectElementIndicesImpl(object, backing_store,
                                                    keys);
4250 4251
  }

4252 4253 4254 4255
  static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
                                         uint32_t capacity) {
    Handle<FixedArrayBase> old_elements(object->elements());
    ElementsKind from_kind = object->GetElementsKind();
4256 4257 4258 4259 4260
    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.
4261
      object->GetIsolate()->UpdateNoElementsProtectorOnSetLength(object);
4262
    }
4263 4264 4265 4266 4267 4268 4269 4270 4271
    // 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);
  }

4272 4273 4274 4275
  static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
                               FixedArrayBase* to, ElementsKind from_kind,
                               uint32_t to_start, int packed_size,
                               int copy_size) {
4276 4277
    DCHECK(!to->IsDictionary());
    if (from_kind == SLOW_STRING_WRAPPER_ELEMENTS) {
4278
      CopyDictionaryToObjectElements(from, from_start, to, HOLEY_ELEMENTS,
4279 4280 4281
                                     to_start, copy_size);
    } else {
      DCHECK_EQ(FAST_STRING_WRAPPER_ELEMENTS, from_kind);
4282 4283
      CopyObjectToObjectElements(from, HOLEY_ELEMENTS, from_start, to,
                                 HOLEY_ELEMENTS, to_start, copy_size);
4284
    }
4285 4286
  }

4287 4288 4289 4290 4291 4292 4293
  static uint32_t NumberOfElementsImpl(JSObject* object,
                                       FixedArrayBase* backing_store) {
    uint32_t length = GetString(object)->length();
    return length +
           BackingStoreAccessor::NumberOfElementsImpl(object, backing_store);
  }

4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311
 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) {}
4312

4313
  static Handle<NumberDictionary> NormalizeImpl(
4314 4315 4316
      Handle<JSObject> object, Handle<FixedArrayBase> elements) {
    return FastHoleyObjectElementsAccessor::NormalizeImpl(object, elements);
  }
4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327
};

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) {}
4328 4329 4330 4331 4332

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

4335 4336 4337
}  // namespace


4338
void CheckArrayAbuse(Handle<JSObject> obj, const char* op, uint32_t index,
4339 4340
                     bool allow_appending) {
  DisallowHeapAllocation no_allocation;
4341
  Object* raw_length = nullptr;
4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356
  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++;
4357
      if (index >= compare_length) {
4358 4359
        PrintF("[OOB %s %s (%s length = %d, element accessed = %d) in ",
               elements_type, op, elements_type, static_cast<int>(int32_length),
4360
               static_cast<int>(index));
4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374
        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");
  }
}
4375 4376


4377 4378
MaybeHandle<Object> ArrayConstructInitializeElements(Handle<JSArray> array,
                                                     Arguments* args) {
4379 4380 4381 4382 4383
  if (args->length() == 0) {
    // Optimize the case where there are no parameters passed.
    JSArray::Initialize(array, JSArray::kPreallocatedArrayElements);
    return array;

4384
  } else if (args->length() == 1 && args->at(0)->IsNumber()) {
4385
    uint32_t length;
4386
    if (!args->at(0)->ToArrayLength(&length)) {
4387 4388 4389 4390 4391
      return ThrowArrayLengthRangeError(array->GetIsolate());
    }

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

4396
      if (!IsHoleyElementsKind(elements_kind)) {
4397 4398
        elements_kind = GetHoleyElementsKind(elements_kind);
        JSObject::TransitionElementsKind(array, elements_kind);
4399
      }
4400 4401
    } else if (length == 0) {
      JSArray::Initialize(array, JSArray::kPreallocatedArrayElements);
4402 4403 4404 4405
    } else {
      // Take the argument as the length.
      JSArray::Initialize(array, 0);
      JSArray::SetLength(array, length);
4406
    }
4407
    return array;
4408 4409
  }

4410 4411
  Factory* factory = array->GetIsolate()->factory();

4412 4413
  // Set length and elements on the array.
  int number_of_elements = args->length();
4414 4415
  JSObject::EnsureCanContainElements(
      array, args, 0, number_of_elements, ALLOW_CONVERTED_DOUBLE_ELEMENTS);
4416 4417 4418

  // Allocate an appropriately typed elements array.
  ElementsKind elements_kind = array->GetElementsKind();
4419
  Handle<FixedArrayBase> elms;
4420
  if (IsDoubleElementsKind(elements_kind)) {
4421 4422
    elms = Handle<FixedArrayBase>::cast(
        factory->NewFixedDoubleArray(number_of_elements));
4423
  } else {
4424 4425
    elms = Handle<FixedArrayBase>::cast(
        factory->NewFixedArrayWithHoles(number_of_elements));
4426 4427 4428
  }

  // Fill in the content
4429
  switch (elements_kind) {
4430 4431
    case HOLEY_SMI_ELEMENTS:
    case PACKED_SMI_ELEMENTS: {
4432
      Handle<FixedArray> smi_elms = Handle<FixedArray>::cast(elms);
4433 4434
      for (int entry = 0; entry < number_of_elements; entry++) {
        smi_elms->set(entry, (*args)[entry], SKIP_WRITE_BARRIER);
4435 4436 4437
      }
      break;
    }
4438 4439
    case HOLEY_ELEMENTS:
    case PACKED_ELEMENTS: {
4440
      DisallowHeapAllocation no_gc;
4441
      WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
4442
      Handle<FixedArray> object_elms = Handle<FixedArray>::cast(elms);
4443 4444
      for (int entry = 0; entry < number_of_elements; entry++) {
        object_elms->set(entry, (*args)[entry], mode);
4445 4446 4447
      }
      break;
    }
4448 4449
    case HOLEY_DOUBLE_ELEMENTS:
    case PACKED_DOUBLE_ELEMENTS: {
4450 4451
      Handle<FixedDoubleArray> double_elms =
          Handle<FixedDoubleArray>::cast(elms);
4452 4453
      for (int entry = 0; entry < number_of_elements; entry++) {
        double_elms->set(entry, (*args)[entry]->Number());
4454 4455 4456 4457 4458 4459 4460 4461
      }
      break;
    }
    default:
      UNREACHABLE();
      break;
  }

4462
  array->set_elements(*elms);
4463 4464 4465 4466
  array->set_length(Smi::FromInt(number_of_elements));
  return array;
}

4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482
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)
4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498
#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)
4499 4500 4501 4502 4503
#undef TYPED_ARRAYS_CASE
    default:
      UNREACHABLE();
  }
}
4504

4505 4506 4507 4508 4509 4510 4511
void CopyTypedArrayElementsSlice(JSTypedArray* source,
                                 JSTypedArray* destination, uintptr_t start,
                                 uintptr_t end) {
  destination->GetElementsAccessor()->CopyTypedArrayElementsSlice(
      source, destination, start, end);
}

4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526
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() {
4527
  if (elements_accessors_ == nullptr) return;
4528 4529 4530
#define ACCESSOR_DELETE(Class, Kind, Store) delete elements_accessors_[Kind];
  ELEMENTS_LIST(ACCESSOR_DELETE)
#undef ACCESSOR_DELETE
4531
  elements_accessors_ = nullptr;
4532 4533
}

4534
Handle<JSArray> ElementsAccessor::Concat(Isolate* isolate, Arguments* args,
4535 4536
                                         uint32_t concat_size,
                                         uint32_t result_len) {
4537
  ElementsKind result_elements_kind = GetInitialFastElementsKind();
4538
  bool has_raw_doubles = false;
4539 4540
  {
    DisallowHeapAllocation no_gc;
4541
    bool is_holey = false;
4542
    for (uint32_t i = 0; i < concat_size; i++) {
4543 4544
      Object* arg = (*args)[i];
      ElementsKind arg_kind = JSArray::cast(arg)->GetElementsKind();
4545 4546
      has_raw_doubles = has_raw_doubles || IsDoubleElementsKind(arg_kind);
      is_holey = is_holey || IsHoleyElementsKind(arg_kind);
4547 4548
      result_elements_kind =
          GetMoreGeneralElementsKind(result_elements_kind, arg_kind);
4549 4550
    }
    if (is_holey) {
4551
      result_elements_kind = GetHoleyElementsKind(result_elements_kind);
4552 4553 4554 4555 4556 4557
    }
  }

  // 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.
4558
  bool requires_double_boxing =
4559
      has_raw_doubles && !IsDoubleElementsKind(result_elements_kind);
4560 4561 4562
  ArrayStorageAllocationMode mode = requires_double_boxing
                                        ? INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE
                                        : DONT_INITIALIZE_ARRAY_ELEMENTS;
4563
  Handle<JSArray> result_array = isolate->factory()->NewJSArray(
4564
      result_elements_kind, result_len, result_len, mode);
4565
  if (result_len == 0) return result_array;
4566 4567

  uint32_t insertion_index = 0;
4568
  Handle<FixedArrayBase> storage(result_array->elements(), isolate);
4569
  ElementsAccessor* accessor = ElementsAccessor::ForKind(result_elements_kind);
4570 4571 4572 4573
  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]);
4574 4575 4576 4577 4578 4579
    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;
4580 4581
  }

4582
  DCHECK_EQ(insertion_index, result_len);
4583 4584 4585
  return result_array;
}

4586
ElementsAccessor** ElementsAccessor::elements_accessors_ = nullptr;
4587 4588
}  // namespace internal
}  // namespace v8