1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/objects/elements.h"
6
7 #include "src/common/message-template.h"
8 #include "src/execution/arguments.h"
9 #include "src/execution/frames.h"
10 #include "src/execution/isolate-inl.h"
11 #include "src/execution/protectors-inl.h"
12 #include "src/heap/factory.h"
13 #include "src/heap/heap-inl.h" // For MaxNumberToStringCacheSize.
14 #include "src/heap/heap-write-barrier-inl.h"
15 #include "src/numbers/conversions.h"
16 #include "src/objects/arguments-inl.h"
17 #include "src/objects/hash-table-inl.h"
18 #include "src/objects/js-array-buffer-inl.h"
19 #include "src/objects/js-array-inl.h"
20 #include "src/objects/keys.h"
21 #include "src/objects/objects-inl.h"
22 #include "src/objects/slots-atomic-inl.h"
23 #include "src/objects/slots.h"
24 #include "src/utils/utils.h"
25
26 // Each concrete ElementsAccessor can handle exactly one ElementsKind,
27 // several abstract ElementsAccessor classes are used to allow sharing
28 // common code.
29 //
30 // Inheritance hierarchy:
31 // - ElementsAccessorBase (abstract)
32 // - FastElementsAccessor (abstract)
33 // - FastSmiOrObjectElementsAccessor
34 // - FastPackedSmiElementsAccessor
35 // - FastHoleySmiElementsAccessor
36 // - FastPackedObjectElementsAccessor
37 // - FastNonextensibleObjectElementsAccessor: template
38 // - FastPackedNonextensibleObjectElementsAccessor
39 // - FastHoleyNonextensibleObjectElementsAccessor
40 // - FastSealedObjectElementsAccessor: template
41 // - FastPackedSealedObjectElementsAccessor
42 // - FastHoleySealedObjectElementsAccessor
43 // - FastFrozenObjectElementsAccessor: template
44 // - FastPackedFrozenObjectElementsAccessor
45 // - FastHoleyFrozenObjectElementsAccessor
46 // - FastHoleyObjectElementsAccessor
47 // - FastDoubleElementsAccessor
48 // - FastPackedDoubleElementsAccessor
49 // - FastHoleyDoubleElementsAccessor
50 // - TypedElementsAccessor: template, with instantiations:
51 // - Uint8ElementsAccessor
52 // - Int8ElementsAccessor
53 // - Uint16ElementsAccessor
54 // - Int16ElementsAccessor
55 // - Uint32ElementsAccessor
56 // - Int32ElementsAccessor
57 // - Float32ElementsAccessor
58 // - Float64ElementsAccessor
59 // - Uint8ClampedElementsAccessor
60 // - BigUint64ElementsAccessor
61 // - BigInt64ElementsAccessor
62 // - DictionaryElementsAccessor
63 // - SloppyArgumentsElementsAccessor
64 // - FastSloppyArgumentsElementsAccessor
65 // - SlowSloppyArgumentsElementsAccessor
66 // - StringWrapperElementsAccessor
67 // - FastStringWrapperElementsAccessor
68 // - SlowStringWrapperElementsAccessor
69
70 namespace v8 {
71 namespace internal {
72
73 namespace {
74
75 #define RETURN_NOTHING_IF_NOT_SUCCESSFUL(call) \
76 do { \
77 if (!(call)) return Nothing<bool>(); \
78 } while (false)
79
80 #define RETURN_FAILURE_IF_NOT_SUCCESSFUL(call) \
81 do { \
82 ExceptionStatus status_enum_result = (call); \
83 if (!status_enum_result) return status_enum_result; \
84 } while (false)
85
86 static const int kPackedSizeNotKnown = -1;
87
88 enum Where { AT_START, AT_END };
89
90 // First argument in list is the accessor class, the second argument is the
91 // accessor ElementsKind, and the third is the backing store class. Use the
92 // fast element handler for smi-only arrays. The implementation is currently
93 // identical. Note that the order must match that of the ElementsKind enum for
94 // the |accessor_array[]| below to work.
95 #define ELEMENTS_LIST(V) \
96 V(FastPackedSmiElementsAccessor, PACKED_SMI_ELEMENTS, FixedArray) \
97 V(FastHoleySmiElementsAccessor, HOLEY_SMI_ELEMENTS, FixedArray) \
98 V(FastPackedObjectElementsAccessor, PACKED_ELEMENTS, FixedArray) \
99 V(FastHoleyObjectElementsAccessor, HOLEY_ELEMENTS, FixedArray) \
100 V(FastPackedDoubleElementsAccessor, PACKED_DOUBLE_ELEMENTS, \
101 FixedDoubleArray) \
102 V(FastHoleyDoubleElementsAccessor, HOLEY_DOUBLE_ELEMENTS, FixedDoubleArray) \
103 V(FastPackedNonextensibleObjectElementsAccessor, \
104 PACKED_NONEXTENSIBLE_ELEMENTS, FixedArray) \
105 V(FastHoleyNonextensibleObjectElementsAccessor, \
106 HOLEY_NONEXTENSIBLE_ELEMENTS, FixedArray) \
107 V(FastPackedSealedObjectElementsAccessor, PACKED_SEALED_ELEMENTS, \
108 FixedArray) \
109 V(FastHoleySealedObjectElementsAccessor, HOLEY_SEALED_ELEMENTS, FixedArray) \
110 V(FastPackedFrozenObjectElementsAccessor, PACKED_FROZEN_ELEMENTS, \
111 FixedArray) \
112 V(FastHoleyFrozenObjectElementsAccessor, HOLEY_FROZEN_ELEMENTS, FixedArray) \
113 V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, NumberDictionary) \
114 V(FastSloppyArgumentsElementsAccessor, FAST_SLOPPY_ARGUMENTS_ELEMENTS, \
115 FixedArray) \
116 V(SlowSloppyArgumentsElementsAccessor, SLOW_SLOPPY_ARGUMENTS_ELEMENTS, \
117 FixedArray) \
118 V(FastStringWrapperElementsAccessor, FAST_STRING_WRAPPER_ELEMENTS, \
119 FixedArray) \
120 V(SlowStringWrapperElementsAccessor, SLOW_STRING_WRAPPER_ELEMENTS, \
121 FixedArray) \
122 V(Uint8ElementsAccessor, UINT8_ELEMENTS, ByteArray) \
123 V(Int8ElementsAccessor, INT8_ELEMENTS, ByteArray) \
124 V(Uint16ElementsAccessor, UINT16_ELEMENTS, ByteArray) \
125 V(Int16ElementsAccessor, INT16_ELEMENTS, ByteArray) \
126 V(Uint32ElementsAccessor, UINT32_ELEMENTS, ByteArray) \
127 V(Int32ElementsAccessor, INT32_ELEMENTS, ByteArray) \
128 V(Float32ElementsAccessor, FLOAT32_ELEMENTS, ByteArray) \
129 V(Float64ElementsAccessor, FLOAT64_ELEMENTS, ByteArray) \
130 V(Uint8ClampedElementsAccessor, UINT8_CLAMPED_ELEMENTS, ByteArray) \
131 V(BigUint64ElementsAccessor, BIGUINT64_ELEMENTS, ByteArray) \
132 V(BigInt64ElementsAccessor, BIGINT64_ELEMENTS, ByteArray)
133
134 template <ElementsKind Kind>
135 class ElementsKindTraits {
136 public:
137 using BackingStore = FixedArrayBase;
138 };
139
140 #define ELEMENTS_TRAITS(Class, KindParam, Store) \
141 template <> \
142 class ElementsKindTraits<KindParam> { \
143 public: /* NOLINT */ \
144 static constexpr ElementsKind Kind = KindParam; \
145 using BackingStore = Store; \
146 }; \
147 constexpr ElementsKind ElementsKindTraits<KindParam>::Kind;
ELEMENTS_LIST(ELEMENTS_TRAITS)148 ELEMENTS_LIST(ELEMENTS_TRAITS)
149 #undef ELEMENTS_TRAITS
150
151 V8_WARN_UNUSED_RESULT
152 MaybeHandle<Object> ThrowArrayLengthRangeError(Isolate* isolate) {
153 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidArrayLength),
154 Object);
155 }
156
GetWriteBarrierMode(ElementsKind kind)157 WriteBarrierMode GetWriteBarrierMode(ElementsKind kind) {
158 if (IsSmiElementsKind(kind)) return SKIP_WRITE_BARRIER;
159 if (IsDoubleElementsKind(kind)) return SKIP_WRITE_BARRIER;
160 return UPDATE_WRITE_BARRIER;
161 }
162
163 // If kCopyToEndAndInitializeToHole is specified as the copy_size to
164 // CopyElements, it copies all of elements from source after source_start to
165 // destination array, padding any remaining uninitialized elements in the
166 // destination array with the hole.
167 constexpr int kCopyToEndAndInitializeToHole = -1;
168
CopyObjectToObjectElements(Isolate * isolate,FixedArrayBase from_base,ElementsKind from_kind,uint32_t from_start,FixedArrayBase to_base,ElementsKind to_kind,uint32_t to_start,int raw_copy_size)169 void CopyObjectToObjectElements(Isolate* isolate, FixedArrayBase from_base,
170 ElementsKind from_kind, uint32_t from_start,
171 FixedArrayBase to_base, ElementsKind to_kind,
172 uint32_t to_start, int raw_copy_size) {
173 ReadOnlyRoots roots(isolate);
174 DCHECK(to_base.map() != roots.fixed_cow_array_map());
175 DisallowHeapAllocation no_allocation;
176 int copy_size = raw_copy_size;
177 if (raw_copy_size < 0) {
178 DCHECK_EQ(kCopyToEndAndInitializeToHole, raw_copy_size);
179 copy_size =
180 Min(from_base.length() - from_start, to_base.length() - to_start);
181 int start = to_start + copy_size;
182 int length = to_base.length() - start;
183 if (length > 0) {
184 MemsetTagged(FixedArray::cast(to_base).RawFieldOfElementAt(start),
185 roots.the_hole_value(), length);
186 }
187 }
188 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base.length() &&
189 (copy_size + static_cast<int>(from_start)) <= from_base.length());
190 if (copy_size == 0) return;
191 FixedArray from = FixedArray::cast(from_base);
192 FixedArray to = FixedArray::cast(to_base);
193 DCHECK(IsSmiOrObjectElementsKind(from_kind));
194 DCHECK(IsSmiOrObjectElementsKind(to_kind));
195
196 WriteBarrierMode write_barrier_mode =
197 (IsObjectElementsKind(from_kind) && IsObjectElementsKind(to_kind))
198 ? UPDATE_WRITE_BARRIER
199 : SKIP_WRITE_BARRIER;
200 to.CopyElements(isolate, to_start, from, from_start, copy_size,
201 write_barrier_mode);
202 }
203
CopyDictionaryToObjectElements(Isolate * isolate,FixedArrayBase from_base,uint32_t from_start,FixedArrayBase to_base,ElementsKind to_kind,uint32_t to_start,int raw_copy_size)204 void CopyDictionaryToObjectElements(Isolate* isolate, FixedArrayBase from_base,
205 uint32_t from_start, FixedArrayBase to_base,
206 ElementsKind to_kind, uint32_t to_start,
207 int raw_copy_size) {
208 DisallowHeapAllocation no_allocation;
209 NumberDictionary from = NumberDictionary::cast(from_base);
210 int copy_size = raw_copy_size;
211 if (raw_copy_size < 0) {
212 DCHECK_EQ(kCopyToEndAndInitializeToHole, raw_copy_size);
213 copy_size = from.max_number_key() + 1 - from_start;
214 int start = to_start + copy_size;
215 int length = to_base.length() - start;
216 if (length > 0) {
217 MemsetTagged(FixedArray::cast(to_base).RawFieldOfElementAt(start),
218 ReadOnlyRoots(isolate).the_hole_value(), length);
219 }
220 }
221 DCHECK(to_base != from_base);
222 DCHECK(IsSmiOrObjectElementsKind(to_kind));
223 if (copy_size == 0) return;
224 FixedArray to = FixedArray::cast(to_base);
225 uint32_t to_length = to.length();
226 if (to_start + copy_size > to_length) {
227 copy_size = to_length - to_start;
228 }
229 WriteBarrierMode write_barrier_mode = GetWriteBarrierMode(to_kind);
230 for (int i = 0; i < copy_size; i++) {
231 InternalIndex entry = from.FindEntry(isolate, i + from_start);
232 if (entry.is_found()) {
233 Object value = from.ValueAt(entry);
234 DCHECK(!value.IsTheHole(isolate));
235 to.set(i + to_start, value, write_barrier_mode);
236 } else {
237 to.set_the_hole(isolate, i + to_start);
238 }
239 }
240 }
241
242 // NOTE: this method violates the handlified function signature convention:
243 // raw pointer parameters in the function that allocates.
244 // See ElementsAccessorBase::CopyElements() for details.
CopyDoubleToObjectElements(Isolate * isolate,FixedArrayBase from_base,uint32_t from_start,FixedArrayBase to_base,uint32_t to_start,int raw_copy_size)245 void CopyDoubleToObjectElements(Isolate* isolate, FixedArrayBase from_base,
246 uint32_t from_start, FixedArrayBase to_base,
247 uint32_t to_start, int raw_copy_size) {
248 int copy_size = raw_copy_size;
249 if (raw_copy_size < 0) {
250 DisallowHeapAllocation no_allocation;
251 DCHECK_EQ(kCopyToEndAndInitializeToHole, raw_copy_size);
252 copy_size =
253 Min(from_base.length() - from_start, to_base.length() - to_start);
254 // Also initialize the area that will be copied over since HeapNumber
255 // allocation below can cause an incremental marking step, requiring all
256 // existing heap objects to be propertly initialized.
257 int start = to_start;
258 int length = to_base.length() - start;
259 if (length > 0) {
260 MemsetTagged(FixedArray::cast(to_base).RawFieldOfElementAt(start),
261 ReadOnlyRoots(isolate).the_hole_value(), length);
262 }
263 }
264
265 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base.length() &&
266 (copy_size + static_cast<int>(from_start)) <= from_base.length());
267 if (copy_size == 0) return;
268
269 // From here on, the code below could actually allocate. Therefore the raw
270 // values are wrapped into handles.
271 Handle<FixedDoubleArray> from(FixedDoubleArray::cast(from_base), isolate);
272 Handle<FixedArray> to(FixedArray::cast(to_base), isolate);
273
274 // Use an outer loop to not waste too much time on creating HandleScopes.
275 // On the other hand we might overflow a single handle scope depending on
276 // the copy_size.
277 int offset = 0;
278 while (offset < copy_size) {
279 HandleScope scope(isolate);
280 offset += 100;
281 for (int i = offset - 100; i < offset && i < copy_size; ++i) {
282 Handle<Object> value =
283 FixedDoubleArray::get(*from, i + from_start, isolate);
284 to->set(i + to_start, *value, UPDATE_WRITE_BARRIER);
285 }
286 }
287 }
288
CopyDoubleToDoubleElements(FixedArrayBase from_base,uint32_t from_start,FixedArrayBase to_base,uint32_t to_start,int raw_copy_size)289 void CopyDoubleToDoubleElements(FixedArrayBase from_base, uint32_t from_start,
290 FixedArrayBase to_base, uint32_t to_start,
291 int raw_copy_size) {
292 DisallowHeapAllocation no_allocation;
293 int copy_size = raw_copy_size;
294 if (raw_copy_size < 0) {
295 DCHECK_EQ(kCopyToEndAndInitializeToHole, raw_copy_size);
296 copy_size =
297 Min(from_base.length() - from_start, to_base.length() - to_start);
298 for (int i = to_start + copy_size; i < to_base.length(); ++i) {
299 FixedDoubleArray::cast(to_base).set_the_hole(i);
300 }
301 }
302 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base.length() &&
303 (copy_size + static_cast<int>(from_start)) <= from_base.length());
304 if (copy_size == 0) return;
305 FixedDoubleArray from = FixedDoubleArray::cast(from_base);
306 FixedDoubleArray to = FixedDoubleArray::cast(to_base);
307 Address to_address = to.address() + FixedDoubleArray::kHeaderSize;
308 Address from_address = from.address() + FixedDoubleArray::kHeaderSize;
309 to_address += kDoubleSize * to_start;
310 from_address += kDoubleSize * from_start;
311 #ifdef V8_COMPRESS_POINTERS
312 // TODO(ishell, v8:8875): we use CopyTagged() in order to avoid unaligned
313 // access to double values in the arrays. This will no longed be necessary
314 // once the allocations alignment issue is fixed.
315 int words_per_double = (kDoubleSize / kTaggedSize);
316 CopyTagged(to_address, from_address,
317 static_cast<size_t>(words_per_double * copy_size));
318 #else
319 int words_per_double = (kDoubleSize / kSystemPointerSize);
320 CopyWords(to_address, from_address,
321 static_cast<size_t>(words_per_double * copy_size));
322 #endif
323 }
324
CopySmiToDoubleElements(FixedArrayBase from_base,uint32_t from_start,FixedArrayBase to_base,uint32_t to_start,int raw_copy_size)325 void CopySmiToDoubleElements(FixedArrayBase from_base, uint32_t from_start,
326 FixedArrayBase to_base, uint32_t to_start,
327 int raw_copy_size) {
328 DisallowHeapAllocation no_allocation;
329 int copy_size = raw_copy_size;
330 if (raw_copy_size < 0) {
331 DCHECK_EQ(kCopyToEndAndInitializeToHole, raw_copy_size);
332 copy_size = from_base.length() - from_start;
333 for (int i = to_start + copy_size; i < to_base.length(); ++i) {
334 FixedDoubleArray::cast(to_base).set_the_hole(i);
335 }
336 }
337 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base.length() &&
338 (copy_size + static_cast<int>(from_start)) <= from_base.length());
339 if (copy_size == 0) return;
340 FixedArray from = FixedArray::cast(from_base);
341 FixedDoubleArray to = FixedDoubleArray::cast(to_base);
342 Object the_hole = from.GetReadOnlyRoots().the_hole_value();
343 for (uint32_t from_end = from_start + static_cast<uint32_t>(copy_size);
344 from_start < from_end; from_start++, to_start++) {
345 Object hole_or_smi = from.get(from_start);
346 if (hole_or_smi == the_hole) {
347 to.set_the_hole(to_start);
348 } else {
349 to.set(to_start, Smi::ToInt(hole_or_smi));
350 }
351 }
352 }
353
CopyPackedSmiToDoubleElements(FixedArrayBase from_base,uint32_t from_start,FixedArrayBase to_base,uint32_t to_start,int packed_size,int raw_copy_size)354 void CopyPackedSmiToDoubleElements(FixedArrayBase from_base,
355 uint32_t from_start, FixedArrayBase to_base,
356 uint32_t to_start, int packed_size,
357 int raw_copy_size) {
358 DisallowHeapAllocation no_allocation;
359 int copy_size = raw_copy_size;
360 uint32_t to_end;
361 if (raw_copy_size < 0) {
362 DCHECK_EQ(kCopyToEndAndInitializeToHole, raw_copy_size);
363 copy_size = packed_size - from_start;
364 to_end = to_base.length();
365 for (uint32_t i = to_start + copy_size; i < to_end; ++i) {
366 FixedDoubleArray::cast(to_base).set_the_hole(i);
367 }
368 } else {
369 to_end = to_start + static_cast<uint32_t>(copy_size);
370 }
371 DCHECK(static_cast<int>(to_end) <= to_base.length());
372 DCHECK(packed_size >= 0 && packed_size <= copy_size);
373 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base.length() &&
374 (copy_size + static_cast<int>(from_start)) <= from_base.length());
375 if (copy_size == 0) return;
376 FixedArray from = FixedArray::cast(from_base);
377 FixedDoubleArray to = FixedDoubleArray::cast(to_base);
378 for (uint32_t from_end = from_start + static_cast<uint32_t>(packed_size);
379 from_start < from_end; from_start++, to_start++) {
380 Object smi = from.get(from_start);
381 DCHECK(!smi.IsTheHole());
382 to.set(to_start, Smi::ToInt(smi));
383 }
384 }
385
CopyObjectToDoubleElements(FixedArrayBase from_base,uint32_t from_start,FixedArrayBase to_base,uint32_t to_start,int raw_copy_size)386 void CopyObjectToDoubleElements(FixedArrayBase from_base, uint32_t from_start,
387 FixedArrayBase to_base, uint32_t to_start,
388 int raw_copy_size) {
389 DisallowHeapAllocation no_allocation;
390 int copy_size = raw_copy_size;
391 if (raw_copy_size < 0) {
392 DCHECK_EQ(kCopyToEndAndInitializeToHole, raw_copy_size);
393 copy_size = from_base.length() - from_start;
394 for (int i = to_start + copy_size; i < to_base.length(); ++i) {
395 FixedDoubleArray::cast(to_base).set_the_hole(i);
396 }
397 }
398 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base.length() &&
399 (copy_size + static_cast<int>(from_start)) <= from_base.length());
400 if (copy_size == 0) return;
401 FixedArray from = FixedArray::cast(from_base);
402 FixedDoubleArray to = FixedDoubleArray::cast(to_base);
403 Object the_hole = from.GetReadOnlyRoots().the_hole_value();
404 for (uint32_t from_end = from_start + copy_size; from_start < from_end;
405 from_start++, to_start++) {
406 Object hole_or_object = from.get(from_start);
407 if (hole_or_object == the_hole) {
408 to.set_the_hole(to_start);
409 } else {
410 to.set(to_start, hole_or_object.Number());
411 }
412 }
413 }
414
CopyDictionaryToDoubleElements(Isolate * isolate,FixedArrayBase from_base,uint32_t from_start,FixedArrayBase to_base,uint32_t to_start,int raw_copy_size)415 void CopyDictionaryToDoubleElements(Isolate* isolate, FixedArrayBase from_base,
416 uint32_t from_start, FixedArrayBase to_base,
417 uint32_t to_start, int raw_copy_size) {
418 DisallowHeapAllocation no_allocation;
419 NumberDictionary from = NumberDictionary::cast(from_base);
420 int copy_size = raw_copy_size;
421 if (copy_size < 0) {
422 DCHECK_EQ(kCopyToEndAndInitializeToHole, copy_size);
423 copy_size = from.max_number_key() + 1 - from_start;
424 for (int i = to_start + copy_size; i < to_base.length(); ++i) {
425 FixedDoubleArray::cast(to_base).set_the_hole(i);
426 }
427 }
428 if (copy_size == 0) return;
429 FixedDoubleArray to = FixedDoubleArray::cast(to_base);
430 uint32_t to_length = to.length();
431 if (to_start + copy_size > to_length) {
432 copy_size = to_length - to_start;
433 }
434 for (int i = 0; i < copy_size; i++) {
435 InternalIndex entry = from.FindEntry(isolate, i + from_start);
436 if (entry.is_found()) {
437 to.set(i + to_start, from.ValueAt(entry).Number());
438 } else {
439 to.set_the_hole(i + to_start);
440 }
441 }
442 }
443
SortIndices(Isolate * isolate,Handle<FixedArray> indices,uint32_t sort_size)444 void SortIndices(Isolate* isolate, Handle<FixedArray> indices,
445 uint32_t sort_size) {
446 // Use AtomicSlot wrapper to ensure that std::sort uses atomic load and
447 // store operations that are safe for concurrent marking.
448 AtomicSlot start(indices->GetFirstElementAddress());
449 AtomicSlot end(start + sort_size);
450 std::sort(start, end, [isolate](Tagged_t elementA, Tagged_t elementB) {
451 #ifdef V8_COMPRESS_POINTERS
452 Object a(DecompressTaggedAny(isolate, elementA));
453 Object b(DecompressTaggedAny(isolate, elementB));
454 #else
455 Object a(elementA);
456 Object b(elementB);
457 #endif
458 if (a.IsSmi() || !a.IsUndefined(isolate)) {
459 if (!b.IsSmi() && b.IsUndefined(isolate)) {
460 return true;
461 }
462 return a.Number() < b.Number();
463 }
464 return !b.IsSmi() && b.IsUndefined(isolate);
465 });
466 isolate->heap()->WriteBarrierForRange(*indices, ObjectSlot(start),
467 ObjectSlot(end));
468 }
469
IncludesValueSlowPath(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)470 Maybe<bool> IncludesValueSlowPath(Isolate* isolate, Handle<JSObject> receiver,
471 Handle<Object> value, size_t start_from,
472 size_t length) {
473 bool search_for_hole = value->IsUndefined(isolate);
474 for (size_t k = start_from; k < length; ++k) {
475 LookupIterator it(isolate, receiver, k);
476 if (!it.IsFound()) {
477 if (search_for_hole) return Just(true);
478 continue;
479 }
480 Handle<Object> element_k;
481 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k,
482 Object::GetProperty(&it), Nothing<bool>());
483
484 if (value->SameValueZero(*element_k)) return Just(true);
485 }
486
487 return Just(false);
488 }
489
IndexOfValueSlowPath(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)490 Maybe<int64_t> IndexOfValueSlowPath(Isolate* isolate, Handle<JSObject> receiver,
491 Handle<Object> value, size_t start_from,
492 size_t length) {
493 for (size_t k = start_from; k < length; ++k) {
494 LookupIterator it(isolate, receiver, k);
495 if (!it.IsFound()) {
496 continue;
497 }
498 Handle<Object> element_k;
499 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
500 isolate, element_k, Object::GetProperty(&it), Nothing<int64_t>());
501
502 if (value->StrictEquals(*element_k)) return Just<int64_t>(k);
503 }
504
505 return Just<int64_t>(-1);
506 }
507
508 // The InternalElementsAccessor is a helper class to expose otherwise protected
509 // methods to its subclasses. Namely, we don't want to publicly expose methods
510 // that take an entry (instead of an index) as an argument.
511 class InternalElementsAccessor : public ElementsAccessor {
512 public:
513 InternalIndex GetEntryForIndex(Isolate* isolate, JSObject holder,
514 FixedArrayBase backing_store,
515 size_t index) override = 0;
516
517 PropertyDetails GetDetails(JSObject holder, InternalIndex entry) override = 0;
518 };
519
520 // Base class for element handler implementations. Contains the
521 // the common logic for objects with different ElementsKinds.
522 // Subclasses must specialize method for which the element
523 // implementation differs from the base class implementation.
524 //
525 // This class is intended to be used in the following way:
526 //
527 // class SomeElementsAccessor :
528 // public ElementsAccessorBase<SomeElementsAccessor,
529 // BackingStoreClass> {
530 // ...
531 // }
532 //
533 // This is an example of the Curiously Recurring Template Pattern (see
534 // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use
535 // CRTP to guarantee aggressive compile time optimizations (i.e. inlining and
536 // specialization of SomeElementsAccessor methods).
537 template <typename Subclass, typename ElementsTraitsParam>
538 class ElementsAccessorBase : public InternalElementsAccessor {
539 public:
540 ElementsAccessorBase() = default;
541
542 using ElementsTraits = ElementsTraitsParam;
543 using BackingStore = typename ElementsTraitsParam::BackingStore;
544
kind()545 static ElementsKind kind() { return ElementsTraits::Kind; }
546
ValidateContents(JSObject holder,size_t length)547 static void ValidateContents(JSObject holder, size_t length) {}
548
ValidateImpl(JSObject holder)549 static void ValidateImpl(JSObject holder) {
550 FixedArrayBase fixed_array_base = holder.elements();
551 if (!fixed_array_base.IsHeapObject()) return;
552 // Arrays that have been shifted in place can't be verified.
553 if (fixed_array_base.IsFreeSpaceOrFiller()) return;
554 size_t length = 0;
555 if (holder.IsJSArray()) {
556 Object length_obj = JSArray::cast(holder).length();
557 if (length_obj.IsSmi()) {
558 length = Smi::ToInt(length_obj);
559 }
560 } else if (holder.IsJSTypedArray()) {
561 length = JSTypedArray::cast(holder).length();
562 } else {
563 length = fixed_array_base.length();
564 }
565 Subclass::ValidateContents(holder, length);
566 }
567
Validate(JSObject holder)568 void Validate(JSObject holder) final {
569 DisallowHeapAllocation no_gc;
570 Subclass::ValidateImpl(holder);
571 }
572
HasElement(JSObject holder,uint32_t index,FixedArrayBase backing_store,PropertyFilter filter)573 bool HasElement(JSObject holder, uint32_t index, FixedArrayBase backing_store,
574 PropertyFilter filter) final {
575 return Subclass::HasElementImpl(holder.GetIsolate(), holder, index,
576 backing_store, filter);
577 }
578
HasElementImpl(Isolate * isolate,JSObject holder,size_t index,FixedArrayBase backing_store,PropertyFilter filter=ALL_PROPERTIES)579 static bool HasElementImpl(Isolate* isolate, JSObject holder, size_t index,
580 FixedArrayBase backing_store,
581 PropertyFilter filter = ALL_PROPERTIES) {
582 return Subclass::GetEntryForIndexImpl(isolate, holder, backing_store, index,
583 filter)
584 .is_found();
585 }
586
HasEntry(JSObject holder,InternalIndex entry)587 bool HasEntry(JSObject holder, InternalIndex entry) final {
588 return Subclass::HasEntryImpl(holder.GetIsolate(), holder.elements(),
589 entry);
590 }
591
HasEntryImpl(Isolate * isolate,FixedArrayBase backing_store,InternalIndex entry)592 static bool HasEntryImpl(Isolate* isolate, FixedArrayBase backing_store,
593 InternalIndex entry) {
594 UNIMPLEMENTED();
595 }
596
HasAccessors(JSObject holder)597 bool HasAccessors(JSObject holder) final {
598 return Subclass::HasAccessorsImpl(holder, holder.elements());
599 }
600
HasAccessorsImpl(JSObject holder,FixedArrayBase backing_store)601 static bool HasAccessorsImpl(JSObject holder, FixedArrayBase backing_store) {
602 return false;
603 }
604
Get(Handle<JSObject> holder,InternalIndex entry)605 Handle<Object> Get(Handle<JSObject> holder, InternalIndex entry) final {
606 return Subclass::GetInternalImpl(holder, entry);
607 }
608
GetInternalImpl(Handle<JSObject> holder,InternalIndex entry)609 static Handle<Object> GetInternalImpl(Handle<JSObject> holder,
610 InternalIndex entry) {
611 return Subclass::GetImpl(holder->GetIsolate(), holder->elements(), entry);
612 }
613
GetImpl(Isolate * isolate,FixedArrayBase backing_store,InternalIndex entry)614 static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase backing_store,
615 InternalIndex entry) {
616 return handle(BackingStore::cast(backing_store).get(entry.as_int()),
617 isolate);
618 }
619
Set(Handle<JSObject> holder,InternalIndex entry,Object value)620 void Set(Handle<JSObject> holder, InternalIndex entry, Object value) final {
621 Subclass::SetImpl(holder, entry, value);
622 }
623
Reconfigure(Handle<JSObject> object,Handle<FixedArrayBase> store,InternalIndex entry,Handle<Object> value,PropertyAttributes attributes)624 void Reconfigure(Handle<JSObject> object, Handle<FixedArrayBase> store,
625 InternalIndex entry, Handle<Object> value,
626 PropertyAttributes attributes) final {
627 Subclass::ReconfigureImpl(object, store, entry, value, attributes);
628 }
629
ReconfigureImpl(Handle<JSObject> object,Handle<FixedArrayBase> store,InternalIndex entry,Handle<Object> value,PropertyAttributes attributes)630 static void ReconfigureImpl(Handle<JSObject> object,
631 Handle<FixedArrayBase> store, InternalIndex entry,
632 Handle<Object> value,
633 PropertyAttributes attributes) {
634 UNREACHABLE();
635 }
636
Add(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)637 void Add(Handle<JSObject> object, uint32_t index, Handle<Object> value,
638 PropertyAttributes attributes, uint32_t new_capacity) final {
639 Subclass::AddImpl(object, index, value, attributes, new_capacity);
640 }
641
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)642 static void AddImpl(Handle<JSObject> object, uint32_t index,
643 Handle<Object> value, PropertyAttributes attributes,
644 uint32_t new_capacity) {
645 UNREACHABLE();
646 }
647
Push(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t push_size)648 uint32_t Push(Handle<JSArray> receiver, BuiltinArguments* args,
649 uint32_t push_size) final {
650 return Subclass::PushImpl(receiver, args, push_size);
651 }
652
PushImpl(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t push_sized)653 static uint32_t PushImpl(Handle<JSArray> receiver, BuiltinArguments* args,
654 uint32_t push_sized) {
655 UNREACHABLE();
656 }
657
Unshift(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t unshift_size)658 uint32_t Unshift(Handle<JSArray> receiver, BuiltinArguments* args,
659 uint32_t unshift_size) final {
660 return Subclass::UnshiftImpl(receiver, args, unshift_size);
661 }
662
UnshiftImpl(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t unshift_size)663 static uint32_t UnshiftImpl(Handle<JSArray> receiver, BuiltinArguments* args,
664 uint32_t unshift_size) {
665 UNREACHABLE();
666 }
667
Pop(Handle<JSArray> receiver)668 Handle<Object> Pop(Handle<JSArray> receiver) final {
669 return Subclass::PopImpl(receiver);
670 }
671
PopImpl(Handle<JSArray> receiver)672 static Handle<Object> PopImpl(Handle<JSArray> receiver) { UNREACHABLE(); }
673
Shift(Handle<JSArray> receiver)674 Handle<Object> Shift(Handle<JSArray> receiver) final {
675 return Subclass::ShiftImpl(receiver);
676 }
677
ShiftImpl(Handle<JSArray> receiver)678 static Handle<Object> ShiftImpl(Handle<JSArray> receiver) { UNREACHABLE(); }
679
SetLength(Handle<JSArray> array,uint32_t length)680 void SetLength(Handle<JSArray> array, uint32_t length) final {
681 Subclass::SetLengthImpl(array->GetIsolate(), array, length,
682 handle(array->elements(), array->GetIsolate()));
683 }
684
SetLengthImpl(Isolate * isolate,Handle<JSArray> array,uint32_t length,Handle<FixedArrayBase> backing_store)685 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
686 uint32_t length,
687 Handle<FixedArrayBase> backing_store) {
688 DCHECK(!array->SetLengthWouldNormalize(length));
689 DCHECK(IsFastElementsKind(array->GetElementsKind()));
690 uint32_t old_length = 0;
691 CHECK(array->length().ToArrayIndex(&old_length));
692
693 if (old_length < length) {
694 ElementsKind kind = array->GetElementsKind();
695 if (!IsHoleyElementsKind(kind)) {
696 kind = GetHoleyElementsKind(kind);
697 JSObject::TransitionElementsKind(array, kind);
698 }
699 }
700
701 // Check whether the backing store should be shrunk.
702 uint32_t capacity = backing_store->length();
703 old_length = Min(old_length, capacity);
704 if (length == 0) {
705 array->initialize_elements();
706 } else if (length <= capacity) {
707 if (IsSmiOrObjectElementsKind(kind())) {
708 JSObject::EnsureWritableFastElements(array);
709 if (array->elements() != *backing_store) {
710 backing_store = handle(array->elements(), isolate);
711 }
712 }
713 if (2 * length + JSObject::kMinAddedElementsCapacity <= capacity) {
714 // If more than half the elements won't be used, trim the array.
715 // Do not trim from short arrays to prevent frequent trimming on
716 // repeated pop operations.
717 // Leave some space to allow for subsequent push operations.
718 int elements_to_trim = length + 1 == old_length
719 ? (capacity - length) / 2
720 : capacity - length;
721 isolate->heap()->RightTrimFixedArray(*backing_store, elements_to_trim);
722 // Fill the non-trimmed elements with holes.
723 BackingStore::cast(*backing_store)
724 .FillWithHoles(length,
725 std::min(old_length, capacity - elements_to_trim));
726 } else {
727 // Otherwise, fill the unused tail with holes.
728 BackingStore::cast(*backing_store).FillWithHoles(length, old_length);
729 }
730 } else {
731 // Check whether the backing store should be expanded.
732 capacity = Max(length, JSObject::NewElementsCapacity(capacity));
733 Subclass::GrowCapacityAndConvertImpl(array, capacity);
734 }
735
736 array->set_length(Smi::FromInt(length));
737 JSObject::ValidateElements(*array);
738 }
739
NumberOfElements(JSObject receiver)740 size_t NumberOfElements(JSObject receiver) final {
741 return Subclass::NumberOfElementsImpl(receiver, receiver.elements());
742 }
743
NumberOfElementsImpl(JSObject receiver,FixedArrayBase backing_store)744 static uint32_t NumberOfElementsImpl(JSObject receiver,
745 FixedArrayBase backing_store) {
746 UNREACHABLE();
747 }
748
GetMaxIndex(JSObject receiver,FixedArrayBase elements)749 static size_t GetMaxIndex(JSObject receiver, FixedArrayBase elements) {
750 if (receiver.IsJSArray()) {
751 DCHECK(JSArray::cast(receiver).length().IsSmi());
752 return static_cast<uint32_t>(
753 Smi::ToInt(JSArray::cast(receiver).length()));
754 }
755 return Subclass::GetCapacityImpl(receiver, elements);
756 }
757
GetMaxNumberOfEntries(JSObject receiver,FixedArrayBase elements)758 static size_t GetMaxNumberOfEntries(JSObject receiver,
759 FixedArrayBase elements) {
760 return Subclass::GetMaxIndex(receiver, elements);
761 }
762
ConvertElementsWithCapacity(Handle<JSObject> object,Handle<FixedArrayBase> old_elements,ElementsKind from_kind,uint32_t capacity)763 static Handle<FixedArrayBase> ConvertElementsWithCapacity(
764 Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
765 ElementsKind from_kind, uint32_t capacity) {
766 return ConvertElementsWithCapacity(object, old_elements, from_kind,
767 capacity, 0, 0);
768 }
769
ConvertElementsWithCapacity(Handle<JSObject> object,Handle<FixedArrayBase> old_elements,ElementsKind from_kind,uint32_t capacity,uint32_t src_index,uint32_t dst_index)770 static Handle<FixedArrayBase> ConvertElementsWithCapacity(
771 Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
772 ElementsKind from_kind, uint32_t capacity, uint32_t src_index,
773 uint32_t dst_index) {
774 Isolate* isolate = object->GetIsolate();
775 Handle<FixedArrayBase> new_elements;
776 if (IsDoubleElementsKind(kind())) {
777 new_elements = isolate->factory()->NewFixedDoubleArray(capacity);
778 } else {
779 new_elements = isolate->factory()->NewUninitializedFixedArray(capacity);
780 }
781
782 int packed_size = kPackedSizeNotKnown;
783 if (IsFastPackedElementsKind(from_kind) && object->IsJSArray()) {
784 packed_size = Smi::ToInt(JSArray::cast(*object).length());
785 }
786
787 Subclass::CopyElementsImpl(isolate, *old_elements, src_index, *new_elements,
788 from_kind, dst_index, packed_size,
789 kCopyToEndAndInitializeToHole);
790
791 return new_elements;
792 }
793
TransitionElementsKindImpl(Handle<JSObject> object,Handle<Map> to_map)794 static void TransitionElementsKindImpl(Handle<JSObject> object,
795 Handle<Map> to_map) {
796 Isolate* isolate = object->GetIsolate();
797 Handle<Map> from_map = handle(object->map(), isolate);
798 ElementsKind from_kind = from_map->elements_kind();
799 ElementsKind to_kind = to_map->elements_kind();
800 if (IsHoleyElementsKind(from_kind)) {
801 to_kind = GetHoleyElementsKind(to_kind);
802 }
803 if (from_kind != to_kind) {
804 // This method should never be called for any other case.
805 DCHECK(IsFastElementsKind(from_kind));
806 DCHECK(IsFastElementsKind(to_kind));
807 DCHECK_NE(TERMINAL_FAST_ELEMENTS_KIND, from_kind);
808
809 Handle<FixedArrayBase> from_elements(object->elements(), isolate);
810 if (object->elements() == ReadOnlyRoots(isolate).empty_fixed_array() ||
811 IsDoubleElementsKind(from_kind) == IsDoubleElementsKind(to_kind)) {
812 // No change is needed to the elements() buffer, the transition
813 // only requires a map change.
814 JSObject::MigrateToMap(isolate, object, to_map);
815 } else {
816 DCHECK(
817 (IsSmiElementsKind(from_kind) && IsDoubleElementsKind(to_kind)) ||
818 (IsDoubleElementsKind(from_kind) && IsObjectElementsKind(to_kind)));
819 uint32_t capacity = static_cast<uint32_t>(object->elements().length());
820 Handle<FixedArrayBase> elements = ConvertElementsWithCapacity(
821 object, from_elements, from_kind, capacity);
822 JSObject::SetMapAndElements(object, to_map, elements);
823 }
824 if (FLAG_trace_elements_transitions) {
825 JSObject::PrintElementsTransition(stdout, object, from_kind,
826 from_elements, to_kind,
827 handle(object->elements(), isolate));
828 }
829 }
830 }
831
GrowCapacityAndConvertImpl(Handle<JSObject> object,uint32_t capacity)832 static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
833 uint32_t capacity) {
834 ElementsKind from_kind = object->GetElementsKind();
835 if (IsSmiOrObjectElementsKind(from_kind)) {
836 // Array optimizations rely on the prototype lookups of Array objects
837 // always returning undefined. If there is a store to the initial
838 // prototype object, make sure all of these optimizations are invalidated.
839 object->GetIsolate()->UpdateNoElementsProtectorOnSetLength(object);
840 }
841 Handle<FixedArrayBase> old_elements(object->elements(),
842 object->GetIsolate());
843 // This method should only be called if there's a reason to update the
844 // elements.
845 DCHECK(IsDoubleElementsKind(from_kind) != IsDoubleElementsKind(kind()) ||
846 IsDictionaryElementsKind(from_kind) ||
847 static_cast<uint32_t>(old_elements->length()) < capacity);
848 Subclass::BasicGrowCapacityAndConvertImpl(object, old_elements, from_kind,
849 kind(), capacity);
850 }
851
BasicGrowCapacityAndConvertImpl(Handle<JSObject> object,Handle<FixedArrayBase> old_elements,ElementsKind from_kind,ElementsKind to_kind,uint32_t capacity)852 static void BasicGrowCapacityAndConvertImpl(
853 Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
854 ElementsKind from_kind, ElementsKind to_kind, uint32_t capacity) {
855 Handle<FixedArrayBase> elements =
856 ConvertElementsWithCapacity(object, old_elements, from_kind, capacity);
857
858 if (IsHoleyElementsKind(from_kind)) {
859 to_kind = GetHoleyElementsKind(to_kind);
860 }
861 Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, to_kind);
862 JSObject::SetMapAndElements(object, new_map, elements);
863
864 // Transition through the allocation site as well if present.
865 JSObject::UpdateAllocationSite(object, to_kind);
866
867 if (FLAG_trace_elements_transitions) {
868 JSObject::PrintElementsTransition(stdout, object, from_kind, old_elements,
869 to_kind, elements);
870 }
871 }
872
TransitionElementsKind(Handle<JSObject> object,Handle<Map> map)873 void TransitionElementsKind(Handle<JSObject> object, Handle<Map> map) final {
874 Subclass::TransitionElementsKindImpl(object, map);
875 }
876
GrowCapacityAndConvert(Handle<JSObject> object,uint32_t capacity)877 void GrowCapacityAndConvert(Handle<JSObject> object,
878 uint32_t capacity) final {
879 Subclass::GrowCapacityAndConvertImpl(object, capacity);
880 }
881
GrowCapacity(Handle<JSObject> object,uint32_t index)882 bool GrowCapacity(Handle<JSObject> object, uint32_t index) final {
883 // This function is intended to be called from optimized code. We don't
884 // want to trigger lazy deopts there, so refuse to handle cases that would.
885 if (object->map().is_prototype_map() ||
886 object->WouldConvertToSlowElements(index)) {
887 return false;
888 }
889 Handle<FixedArrayBase> old_elements(object->elements(),
890 object->GetIsolate());
891 uint32_t new_capacity = JSObject::NewElementsCapacity(index + 1);
892 DCHECK(static_cast<uint32_t>(old_elements->length()) < new_capacity);
893 Handle<FixedArrayBase> elements =
894 ConvertElementsWithCapacity(object, old_elements, kind(), new_capacity);
895
896 DCHECK_EQ(object->GetElementsKind(), kind());
897 // Transition through the allocation site as well if present.
898 if (JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kCheckOnly>(
899 object, kind())) {
900 return false;
901 }
902
903 object->set_elements(*elements);
904 return true;
905 }
906
Delete(Handle<JSObject> obj,InternalIndex entry)907 void Delete(Handle<JSObject> obj, InternalIndex entry) final {
908 Subclass::DeleteImpl(obj, entry);
909 }
910
CopyElementsImpl(Isolate * isolate,FixedArrayBase from,uint32_t from_start,FixedArrayBase to,ElementsKind from_kind,uint32_t to_start,int packed_size,int copy_size)911 static void CopyElementsImpl(Isolate* isolate, FixedArrayBase from,
912 uint32_t from_start, FixedArrayBase to,
913 ElementsKind from_kind, uint32_t to_start,
914 int packed_size, int copy_size) {
915 UNREACHABLE();
916 }
917
CopyElements(JSObject from_holder,uint32_t from_start,ElementsKind from_kind,Handle<FixedArrayBase> to,uint32_t to_start,int copy_size)918 void CopyElements(JSObject from_holder, uint32_t from_start,
919 ElementsKind from_kind, Handle<FixedArrayBase> to,
920 uint32_t to_start, int copy_size) final {
921 int packed_size = kPackedSizeNotKnown;
922 bool is_packed =
923 IsFastPackedElementsKind(from_kind) && from_holder.IsJSArray();
924 if (is_packed) {
925 packed_size = Smi::ToInt(JSArray::cast(from_holder).length());
926 if (copy_size >= 0 && packed_size > copy_size) {
927 packed_size = copy_size;
928 }
929 }
930 FixedArrayBase from = from_holder.elements();
931 // NOTE: the Subclass::CopyElementsImpl() methods
932 // violate the handlified function signature convention:
933 // raw pointer parameters in the function that allocates. This is done
934 // intentionally to avoid ArrayConcat() builtin performance degradation.
935 //
936 // Details: The idea is that allocations actually happen only in case of
937 // copying from object with fast double elements to object with object
938 // elements. In all the other cases there are no allocations performed and
939 // handle creation causes noticeable performance degradation of the builtin.
940 Subclass::CopyElementsImpl(from_holder.GetIsolate(), from, from_start, *to,
941 from_kind, to_start, packed_size, copy_size);
942 }
943
CopyElements(Isolate * isolate,Handle<FixedArrayBase> source,ElementsKind source_kind,Handle<FixedArrayBase> destination,int size)944 void CopyElements(Isolate* isolate, Handle<FixedArrayBase> source,
945 ElementsKind source_kind,
946 Handle<FixedArrayBase> destination, int size) override {
947 Subclass::CopyElementsImpl(isolate, *source, 0, *destination, source_kind,
948 0, kPackedSizeNotKnown, size);
949 }
950
CopyTypedArrayElementsSlice(JSTypedArray source,JSTypedArray destination,size_t start,size_t end)951 void CopyTypedArrayElementsSlice(JSTypedArray source,
952 JSTypedArray destination, size_t start,
953 size_t end) override {
954 Subclass::CopyTypedArrayElementsSliceImpl(source, destination, start, end);
955 }
956
CopyTypedArrayElementsSliceImpl(JSTypedArray source,JSTypedArray destination,size_t start,size_t end)957 static void CopyTypedArrayElementsSliceImpl(JSTypedArray source,
958 JSTypedArray destination,
959 size_t start, size_t end) {
960 UNREACHABLE();
961 }
962
CopyElements(Handle<Object> source,Handle<JSObject> destination,size_t length,size_t offset)963 Object CopyElements(Handle<Object> source, Handle<JSObject> destination,
964 size_t length, size_t offset) final {
965 return Subclass::CopyElementsHandleImpl(source, destination, length,
966 offset);
967 }
968
CopyElementsHandleImpl(Handle<Object> source,Handle<JSObject> destination,size_t length,size_t offset)969 static Object CopyElementsHandleImpl(Handle<Object> source,
970 Handle<JSObject> destination,
971 size_t length, size_t offset) {
972 UNREACHABLE();
973 }
974
Normalize(Handle<JSObject> object)975 Handle<NumberDictionary> Normalize(Handle<JSObject> object) final {
976 return Subclass::NormalizeImpl(
977 object, handle(object->elements(), object->GetIsolate()));
978 }
979
NormalizeImpl(Handle<JSObject> object,Handle<FixedArrayBase> elements)980 static Handle<NumberDictionary> NormalizeImpl(
981 Handle<JSObject> object, Handle<FixedArrayBase> elements) {
982 UNREACHABLE();
983 }
984
CollectValuesOrEntries(Isolate * isolate,Handle<JSObject> object,Handle<FixedArray> values_or_entries,bool get_entries,int * nof_items,PropertyFilter filter)985 Maybe<bool> CollectValuesOrEntries(Isolate* isolate, Handle<JSObject> object,
986 Handle<FixedArray> values_or_entries,
987 bool get_entries, int* nof_items,
988 PropertyFilter filter) override {
989 return Subclass::CollectValuesOrEntriesImpl(
990 isolate, object, values_or_entries, get_entries, nof_items, filter);
991 }
992
CollectValuesOrEntriesImpl(Isolate * isolate,Handle<JSObject> object,Handle<FixedArray> values_or_entries,bool get_entries,int * nof_items,PropertyFilter filter)993 static Maybe<bool> CollectValuesOrEntriesImpl(
994 Isolate* isolate, Handle<JSObject> object,
995 Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
996 PropertyFilter filter) {
997 DCHECK_EQ(*nof_items, 0);
998 KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
999 ALL_PROPERTIES);
1000 RETURN_NOTHING_IF_NOT_SUCCESSFUL(Subclass::CollectElementIndicesImpl(
1001 object, handle(object->elements(), isolate), &accumulator));
1002 Handle<FixedArray> keys = accumulator.GetKeys();
1003
1004 int count = 0;
1005 int i = 0;
1006 ElementsKind original_elements_kind = object->GetElementsKind();
1007
1008 for (; i < keys->length(); ++i) {
1009 Handle<Object> key(keys->get(i), isolate);
1010 uint32_t index;
1011 if (!key->ToUint32(&index)) continue;
1012
1013 DCHECK_EQ(object->GetElementsKind(), original_elements_kind);
1014 InternalIndex entry = Subclass::GetEntryForIndexImpl(
1015 isolate, *object, object->elements(), index, filter);
1016 if (entry.is_not_found()) continue;
1017 PropertyDetails details = Subclass::GetDetailsImpl(*object, entry);
1018
1019 Handle<Object> value;
1020 if (details.kind() == kData) {
1021 value = Subclass::GetInternalImpl(object, entry);
1022 } else {
1023 // This might modify the elements and/or change the elements kind.
1024 LookupIterator it(isolate, object, index, LookupIterator::OWN);
1025 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
1026 isolate, value, Object::GetProperty(&it), Nothing<bool>());
1027 }
1028 if (get_entries) value = MakeEntryPair(isolate, index, value);
1029 values_or_entries->set(count++, *value);
1030 if (object->GetElementsKind() != original_elements_kind) break;
1031 }
1032
1033 // Slow path caused by changes in elements kind during iteration.
1034 for (; i < keys->length(); i++) {
1035 Handle<Object> key(keys->get(i), isolate);
1036 uint32_t index;
1037 if (!key->ToUint32(&index)) continue;
1038
1039 if (filter & ONLY_ENUMERABLE) {
1040 InternalElementsAccessor* accessor =
1041 reinterpret_cast<InternalElementsAccessor*>(
1042 object->GetElementsAccessor());
1043 InternalIndex entry = accessor->GetEntryForIndex(
1044 isolate, *object, object->elements(), index);
1045 if (entry.is_not_found()) continue;
1046 PropertyDetails details = accessor->GetDetails(*object, entry);
1047 if (!details.IsEnumerable()) continue;
1048 }
1049
1050 Handle<Object> value;
1051 LookupIterator it(isolate, object, index, LookupIterator::OWN);
1052 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::GetProperty(&it),
1053 Nothing<bool>());
1054
1055 if (get_entries) value = MakeEntryPair(isolate, index, value);
1056 values_or_entries->set(count++, *value);
1057 }
1058
1059 *nof_items = count;
1060 return Just(true);
1061 }
1062
CollectElementIndices(Handle<JSObject> object,Handle<FixedArrayBase> backing_store,KeyAccumulator * keys)1063 V8_WARN_UNUSED_RESULT ExceptionStatus CollectElementIndices(
1064 Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
1065 KeyAccumulator* keys) final {
1066 if (keys->filter() & ONLY_ALL_CAN_READ) return ExceptionStatus::kSuccess;
1067 return Subclass::CollectElementIndicesImpl(object, backing_store, keys);
1068 }
1069
CollectElementIndicesImpl(Handle<JSObject> object,Handle<FixedArrayBase> backing_store,KeyAccumulator * keys)1070 V8_WARN_UNUSED_RESULT static ExceptionStatus CollectElementIndicesImpl(
1071 Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
1072 KeyAccumulator* keys) {
1073 DCHECK_NE(DICTIONARY_ELEMENTS, kind());
1074 // Non-dictionary elements can't have all-can-read accessors.
1075 size_t length = Subclass::GetMaxIndex(*object, *backing_store);
1076 PropertyFilter filter = keys->filter();
1077 Isolate* isolate = keys->isolate();
1078 Factory* factory = isolate->factory();
1079 for (size_t i = 0; i < length; i++) {
1080 if (Subclass::HasElementImpl(isolate, *object, i, *backing_store,
1081 filter)) {
1082 RETURN_FAILURE_IF_NOT_SUCCESSFUL(
1083 keys->AddKey(factory->NewNumberFromSize(i)));
1084 }
1085 }
1086 return ExceptionStatus::kSuccess;
1087 }
1088
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)1089 static Handle<FixedArray> DirectCollectElementIndicesImpl(
1090 Isolate* isolate, Handle<JSObject> object,
1091 Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
1092 PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices,
1093 uint32_t insertion_index = 0) {
1094 size_t length = Subclass::GetMaxIndex(*object, *backing_store);
1095 uint32_t const kMaxStringTableEntries =
1096 isolate->heap()->MaxNumberToStringCacheSize();
1097 for (size_t i = 0; i < length; i++) {
1098 if (Subclass::HasElementImpl(isolate, *object, i, *backing_store,
1099 filter)) {
1100 if (convert == GetKeysConversion::kConvertToString) {
1101 bool use_cache = i < kMaxStringTableEntries;
1102 Handle<String> index_string =
1103 isolate->factory()->SizeToString(i, use_cache);
1104 list->set(insertion_index, *index_string);
1105 } else {
1106 Handle<Object> number = isolate->factory()->NewNumberFromSize(i);
1107 list->set(insertion_index, *number);
1108 }
1109 insertion_index++;
1110 }
1111 }
1112 *nof_indices = insertion_index;
1113 return list;
1114 }
1115
PrependElementIndices(Handle<JSObject> object,Handle<FixedArrayBase> backing_store,Handle<FixedArray> keys,GetKeysConversion convert,PropertyFilter filter)1116 MaybeHandle<FixedArray> PrependElementIndices(
1117 Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
1118 Handle<FixedArray> keys, GetKeysConversion convert,
1119 PropertyFilter filter) final {
1120 return Subclass::PrependElementIndicesImpl(object, backing_store, keys,
1121 convert, filter);
1122 }
1123
PrependElementIndicesImpl(Handle<JSObject> object,Handle<FixedArrayBase> backing_store,Handle<FixedArray> keys,GetKeysConversion convert,PropertyFilter filter)1124 static MaybeHandle<FixedArray> PrependElementIndicesImpl(
1125 Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
1126 Handle<FixedArray> keys, GetKeysConversion convert,
1127 PropertyFilter filter) {
1128 Isolate* isolate = object->GetIsolate();
1129 uint32_t nof_property_keys = keys->length();
1130 size_t initial_list_length =
1131 Subclass::GetMaxNumberOfEntries(*object, *backing_store);
1132
1133 if (initial_list_length > FixedArray::kMaxLength - nof_property_keys) {
1134 return isolate->Throw<FixedArray>(isolate->factory()->NewRangeError(
1135 MessageTemplate::kInvalidArrayLength));
1136 }
1137 initial_list_length += nof_property_keys;
1138
1139 // Collect the element indices into a new list.
1140 DCHECK_LE(initial_list_length, std::numeric_limits<int>::max());
1141 MaybeHandle<FixedArray> raw_array = isolate->factory()->TryNewFixedArray(
1142 static_cast<int>(initial_list_length));
1143 Handle<FixedArray> combined_keys;
1144
1145 // If we have a holey backing store try to precisely estimate the backing
1146 // store size as a last emergency measure if we cannot allocate the big
1147 // array.
1148 if (!raw_array.ToHandle(&combined_keys)) {
1149 if (IsHoleyOrDictionaryElementsKind(kind())) {
1150 // If we overestimate the result list size we might end up in the
1151 // large-object space which doesn't free memory on shrinking the list.
1152 // Hence we try to estimate the final size for holey backing stores more
1153 // precisely here.
1154 initial_list_length =
1155 Subclass::NumberOfElementsImpl(*object, *backing_store);
1156 initial_list_length += nof_property_keys;
1157 }
1158 DCHECK_LE(initial_list_length, std::numeric_limits<int>::max());
1159 combined_keys = isolate->factory()->NewFixedArray(
1160 static_cast<int>(initial_list_length));
1161 }
1162
1163 uint32_t nof_indices = 0;
1164 bool needs_sorting = IsDictionaryElementsKind(kind()) ||
1165 IsSloppyArgumentsElementsKind(kind());
1166 combined_keys = Subclass::DirectCollectElementIndicesImpl(
1167 isolate, object, backing_store,
1168 needs_sorting ? GetKeysConversion::kKeepNumbers : convert, filter,
1169 combined_keys, &nof_indices);
1170
1171 if (needs_sorting) {
1172 SortIndices(isolate, combined_keys, nof_indices);
1173 // Indices from dictionary elements should only be converted after
1174 // sorting.
1175 if (convert == GetKeysConversion::kConvertToString) {
1176 for (uint32_t i = 0; i < nof_indices; i++) {
1177 Handle<Object> index_string = isolate->factory()->Uint32ToString(
1178 combined_keys->get(i).Number());
1179 combined_keys->set(i, *index_string);
1180 }
1181 }
1182 }
1183
1184 // Copy over the passed-in property keys.
1185 CopyObjectToObjectElements(isolate, *keys, PACKED_ELEMENTS, 0,
1186 *combined_keys, PACKED_ELEMENTS, nof_indices,
1187 nof_property_keys);
1188
1189 // For holey elements and arguments we might have to shrink the collected
1190 // keys since the estimates might be off.
1191 if (IsHoleyOrDictionaryElementsKind(kind()) ||
1192 IsSloppyArgumentsElementsKind(kind())) {
1193 // Shrink combined_keys to the final size.
1194 int final_size = nof_indices + nof_property_keys;
1195 DCHECK_LE(final_size, combined_keys->length());
1196 return FixedArray::ShrinkOrEmpty(isolate, combined_keys, final_size);
1197 }
1198
1199 return combined_keys;
1200 }
1201
AddElementsToKeyAccumulator(Handle<JSObject> receiver,KeyAccumulator * accumulator,AddKeyConversion convert)1202 V8_WARN_UNUSED_RESULT ExceptionStatus AddElementsToKeyAccumulator(
1203 Handle<JSObject> receiver, KeyAccumulator* accumulator,
1204 AddKeyConversion convert) final {
1205 return Subclass::AddElementsToKeyAccumulatorImpl(receiver, accumulator,
1206 convert);
1207 }
1208
GetCapacityImpl(JSObject holder,FixedArrayBase backing_store)1209 static uint32_t GetCapacityImpl(JSObject holder,
1210 FixedArrayBase backing_store) {
1211 return backing_store.length();
1212 }
1213
GetCapacity(JSObject holder,FixedArrayBase backing_store)1214 size_t GetCapacity(JSObject holder, FixedArrayBase backing_store) final {
1215 return Subclass::GetCapacityImpl(holder, backing_store);
1216 }
1217
FillImpl(Handle<JSObject> receiver,Handle<Object> obj_value,size_t start,size_t end)1218 static Object FillImpl(Handle<JSObject> receiver, Handle<Object> obj_value,
1219 size_t start, size_t end) {
1220 UNREACHABLE();
1221 }
1222
Fill(Handle<JSObject> receiver,Handle<Object> obj_value,size_t start,size_t end)1223 Object Fill(Handle<JSObject> receiver, Handle<Object> obj_value, size_t start,
1224 size_t end) override {
1225 return Subclass::FillImpl(receiver, obj_value, start, end);
1226 }
1227
IncludesValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)1228 static Maybe<bool> IncludesValueImpl(Isolate* isolate,
1229 Handle<JSObject> receiver,
1230 Handle<Object> value, size_t start_from,
1231 size_t length) {
1232 return IncludesValueSlowPath(isolate, receiver, value, start_from, length);
1233 }
1234
IncludesValue(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)1235 Maybe<bool> IncludesValue(Isolate* isolate, Handle<JSObject> receiver,
1236 Handle<Object> value, size_t start_from,
1237 size_t length) final {
1238 return Subclass::IncludesValueImpl(isolate, receiver, value, start_from,
1239 length);
1240 }
1241
IndexOfValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)1242 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
1243 Handle<JSObject> receiver,
1244 Handle<Object> value,
1245 size_t start_from, size_t length) {
1246 return IndexOfValueSlowPath(isolate, receiver, value, start_from, length);
1247 }
1248
IndexOfValue(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)1249 Maybe<int64_t> IndexOfValue(Isolate* isolate, Handle<JSObject> receiver,
1250 Handle<Object> value, size_t start_from,
1251 size_t length) final {
1252 return Subclass::IndexOfValueImpl(isolate, receiver, value, start_from,
1253 length);
1254 }
1255
LastIndexOfValueImpl(Handle<JSObject> receiver,Handle<Object> value,size_t start_from)1256 static Maybe<int64_t> LastIndexOfValueImpl(Handle<JSObject> receiver,
1257 Handle<Object> value,
1258 size_t start_from) {
1259 UNREACHABLE();
1260 }
1261
LastIndexOfValue(Handle<JSObject> receiver,Handle<Object> value,size_t start_from)1262 Maybe<int64_t> LastIndexOfValue(Handle<JSObject> receiver,
1263 Handle<Object> value,
1264 size_t start_from) final {
1265 return Subclass::LastIndexOfValueImpl(receiver, value, start_from);
1266 }
1267
ReverseImpl(JSObject receiver)1268 static void ReverseImpl(JSObject receiver) { UNREACHABLE(); }
1269
Reverse(JSObject receiver)1270 void Reverse(JSObject receiver) final { Subclass::ReverseImpl(receiver); }
1271
GetEntryForIndexImpl(Isolate * isolate,JSObject holder,FixedArrayBase backing_store,size_t index,PropertyFilter filter)1272 static InternalIndex GetEntryForIndexImpl(Isolate* isolate, JSObject holder,
1273 FixedArrayBase backing_store,
1274 size_t index,
1275 PropertyFilter filter) {
1276 DCHECK(IsFastElementsKind(kind()) ||
1277 IsAnyNonextensibleElementsKind(kind()));
1278 size_t length = Subclass::GetMaxIndex(holder, backing_store);
1279 if (IsHoleyElementsKindForRead(kind())) {
1280 DCHECK_IMPLIES(
1281 index < length,
1282 index <= static_cast<size_t>(std::numeric_limits<int>::max()));
1283 return index < length &&
1284 !BackingStore::cast(backing_store)
1285 .is_the_hole(isolate, static_cast<int>(index))
1286 ? InternalIndex(index)
1287 : InternalIndex::NotFound();
1288 } else {
1289 return index < length ? InternalIndex(index) : InternalIndex::NotFound();
1290 }
1291 }
1292
GetEntryForIndex(Isolate * isolate,JSObject holder,FixedArrayBase backing_store,size_t index)1293 InternalIndex GetEntryForIndex(Isolate* isolate, JSObject holder,
1294 FixedArrayBase backing_store,
1295 size_t index) final {
1296 return Subclass::GetEntryForIndexImpl(isolate, holder, backing_store, index,
1297 ALL_PROPERTIES);
1298 }
1299
GetDetailsImpl(FixedArrayBase backing_store,InternalIndex entry)1300 static PropertyDetails GetDetailsImpl(FixedArrayBase backing_store,
1301 InternalIndex entry) {
1302 return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
1303 }
1304
GetDetailsImpl(JSObject holder,InternalIndex entry)1305 static PropertyDetails GetDetailsImpl(JSObject holder, InternalIndex entry) {
1306 return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
1307 }
1308
GetDetails(JSObject holder,InternalIndex entry)1309 PropertyDetails GetDetails(JSObject holder, InternalIndex entry) final {
1310 return Subclass::GetDetailsImpl(holder, entry);
1311 }
1312
CreateListFromArrayLike(Isolate * isolate,Handle<JSObject> object,uint32_t length)1313 Handle<FixedArray> CreateListFromArrayLike(Isolate* isolate,
1314 Handle<JSObject> object,
1315 uint32_t length) final {
1316 return Subclass::CreateListFromArrayLikeImpl(isolate, object, length);
1317 }
1318
CreateListFromArrayLikeImpl(Isolate * isolate,Handle<JSObject> object,uint32_t length)1319 static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
1320 Handle<JSObject> object,
1321 uint32_t length) {
1322 UNREACHABLE();
1323 }
1324
1325 private:
1326 DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase);
1327 };
1328
1329 class DictionaryElementsAccessor
1330 : public ElementsAccessorBase<DictionaryElementsAccessor,
1331 ElementsKindTraits<DICTIONARY_ELEMENTS>> {
1332 public:
GetMaxIndex(JSObject receiver,FixedArrayBase elements)1333 static uint32_t GetMaxIndex(JSObject receiver, FixedArrayBase elements) {
1334 // We cannot properly estimate this for dictionaries.
1335 UNREACHABLE();
1336 }
1337
GetMaxNumberOfEntries(JSObject receiver,FixedArrayBase backing_store)1338 static uint32_t GetMaxNumberOfEntries(JSObject receiver,
1339 FixedArrayBase backing_store) {
1340 return NumberOfElementsImpl(receiver, backing_store);
1341 }
1342
NumberOfElementsImpl(JSObject receiver,FixedArrayBase backing_store)1343 static uint32_t NumberOfElementsImpl(JSObject receiver,
1344 FixedArrayBase backing_store) {
1345 NumberDictionary dict = NumberDictionary::cast(backing_store);
1346 return dict.NumberOfElements();
1347 }
1348
SetLengthImpl(Isolate * isolate,Handle<JSArray> array,uint32_t length,Handle<FixedArrayBase> backing_store)1349 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
1350 uint32_t length,
1351 Handle<FixedArrayBase> backing_store) {
1352 Handle<NumberDictionary> dict =
1353 Handle<NumberDictionary>::cast(backing_store);
1354 uint32_t old_length = 0;
1355 CHECK(array->length().ToArrayLength(&old_length));
1356 {
1357 DisallowHeapAllocation no_gc;
1358 ReadOnlyRoots roots(isolate);
1359 if (length < old_length) {
1360 if (dict->requires_slow_elements()) {
1361 // Find last non-deletable element in range of elements to be
1362 // deleted and adjust range accordingly.
1363 for (InternalIndex entry : dict->IterateEntries()) {
1364 Object index = dict->KeyAt(entry);
1365 if (dict->IsKey(roots, index)) {
1366 uint32_t number = static_cast<uint32_t>(index.Number());
1367 if (length <= number && number < old_length) {
1368 PropertyDetails details = dict->DetailsAt(entry);
1369 if (!details.IsConfigurable()) length = number + 1;
1370 }
1371 }
1372 }
1373 }
1374
1375 if (length == 0) {
1376 // Flush the backing store.
1377 array->initialize_elements();
1378 } else {
1379 // Remove elements that should be deleted.
1380 int removed_entries = 0;
1381 for (InternalIndex entry : dict->IterateEntries()) {
1382 Object index = dict->KeyAt(entry);
1383 if (dict->IsKey(roots, index)) {
1384 uint32_t number = static_cast<uint32_t>(index.Number());
1385 if (length <= number && number < old_length) {
1386 dict->ClearEntry(entry);
1387 removed_entries++;
1388 }
1389 }
1390 }
1391
1392 if (removed_entries > 0) {
1393 // Update the number of elements.
1394 dict->ElementsRemoved(removed_entries);
1395 }
1396 }
1397 }
1398 }
1399
1400 Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length);
1401 array->set_length(*length_obj);
1402 }
1403
CopyElementsImpl(Isolate * isolate,FixedArrayBase from,uint32_t from_start,FixedArrayBase to,ElementsKind from_kind,uint32_t to_start,int packed_size,int copy_size)1404 static void CopyElementsImpl(Isolate* isolate, FixedArrayBase from,
1405 uint32_t from_start, FixedArrayBase to,
1406 ElementsKind from_kind, uint32_t to_start,
1407 int packed_size, int copy_size) {
1408 UNREACHABLE();
1409 }
1410
DeleteImpl(Handle<JSObject> obj,InternalIndex entry)1411 static void DeleteImpl(Handle<JSObject> obj, InternalIndex entry) {
1412 Handle<NumberDictionary> dict(NumberDictionary::cast(obj->elements()),
1413 obj->GetIsolate());
1414 dict = NumberDictionary::DeleteEntry(obj->GetIsolate(), dict, entry);
1415 obj->set_elements(*dict);
1416 }
1417
HasAccessorsImpl(JSObject holder,FixedArrayBase backing_store)1418 static bool HasAccessorsImpl(JSObject holder, FixedArrayBase backing_store) {
1419 DisallowHeapAllocation no_gc;
1420 NumberDictionary dict = NumberDictionary::cast(backing_store);
1421 if (!dict.requires_slow_elements()) return false;
1422 ReadOnlyRoots roots = holder.GetReadOnlyRoots();
1423 for (InternalIndex i : dict.IterateEntries()) {
1424 Object key = dict.KeyAt(i);
1425 if (!dict.IsKey(roots, key)) continue;
1426 PropertyDetails details = dict.DetailsAt(i);
1427 if (details.kind() == kAccessor) return true;
1428 }
1429 return false;
1430 }
1431
GetRaw(FixedArrayBase store,InternalIndex entry)1432 static Object GetRaw(FixedArrayBase store, InternalIndex entry) {
1433 NumberDictionary backing_store = NumberDictionary::cast(store);
1434 return backing_store.ValueAt(entry);
1435 }
1436
GetImpl(Isolate * isolate,FixedArrayBase backing_store,InternalIndex entry)1437 static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase backing_store,
1438 InternalIndex entry) {
1439 return handle(GetRaw(backing_store, entry), isolate);
1440 }
1441
SetImpl(Handle<JSObject> holder,InternalIndex entry,Object value)1442 static inline void SetImpl(Handle<JSObject> holder, InternalIndex entry,
1443 Object value) {
1444 SetImpl(holder->elements(), entry, value);
1445 }
1446
SetImpl(FixedArrayBase backing_store,InternalIndex entry,Object value)1447 static inline void SetImpl(FixedArrayBase backing_store, InternalIndex entry,
1448 Object value) {
1449 NumberDictionary::cast(backing_store).ValueAtPut(entry, value);
1450 }
1451
ReconfigureImpl(Handle<JSObject> object,Handle<FixedArrayBase> store,InternalIndex entry,Handle<Object> value,PropertyAttributes attributes)1452 static void ReconfigureImpl(Handle<JSObject> object,
1453 Handle<FixedArrayBase> store, InternalIndex entry,
1454 Handle<Object> value,
1455 PropertyAttributes attributes) {
1456 NumberDictionary dictionary = NumberDictionary::cast(*store);
1457 if (attributes != NONE) object->RequireSlowElements(dictionary);
1458 dictionary.ValueAtPut(entry, *value);
1459 PropertyDetails details = dictionary.DetailsAt(entry);
1460 details = PropertyDetails(kData, attributes, PropertyCellType::kNoCell,
1461 details.dictionary_index());
1462
1463 dictionary.DetailsAtPut(entry, details);
1464 }
1465
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)1466 static void AddImpl(Handle<JSObject> object, uint32_t index,
1467 Handle<Object> value, PropertyAttributes attributes,
1468 uint32_t new_capacity) {
1469 PropertyDetails details(kData, attributes, PropertyCellType::kNoCell);
1470 Handle<NumberDictionary> dictionary =
1471 object->HasFastElements() || object->HasFastStringWrapperElements()
1472 ? JSObject::NormalizeElements(object)
1473 : handle(NumberDictionary::cast(object->elements()),
1474 object->GetIsolate());
1475 Handle<NumberDictionary> new_dictionary = NumberDictionary::Add(
1476 object->GetIsolate(), dictionary, index, value, details);
1477 new_dictionary->UpdateMaxNumberKey(index, object);
1478 if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
1479 if (dictionary.is_identical_to(new_dictionary)) return;
1480 object->set_elements(*new_dictionary);
1481 }
1482
HasEntryImpl(Isolate * isolate,FixedArrayBase store,InternalIndex entry)1483 static bool HasEntryImpl(Isolate* isolate, FixedArrayBase store,
1484 InternalIndex entry) {
1485 DisallowHeapAllocation no_gc;
1486 NumberDictionary dict = NumberDictionary::cast(store);
1487 Object index = dict.KeyAt(entry);
1488 return !index.IsTheHole(isolate);
1489 }
1490
GetEntryForIndexImpl(Isolate * isolate,JSObject holder,FixedArrayBase store,size_t index,PropertyFilter filter)1491 static InternalIndex GetEntryForIndexImpl(Isolate* isolate, JSObject holder,
1492 FixedArrayBase store, size_t index,
1493 PropertyFilter filter) {
1494 DisallowHeapAllocation no_gc;
1495 NumberDictionary dictionary = NumberDictionary::cast(store);
1496 DCHECK_LE(index, std::numeric_limits<uint32_t>::max());
1497 InternalIndex entry =
1498 dictionary.FindEntry(isolate, static_cast<uint32_t>(index));
1499 if (entry.is_not_found()) return entry;
1500
1501 if (filter != ALL_PROPERTIES) {
1502 PropertyDetails details = dictionary.DetailsAt(entry);
1503 PropertyAttributes attr = details.attributes();
1504 if ((attr & filter) != 0) return InternalIndex::NotFound();
1505 }
1506 return entry;
1507 }
1508
GetDetailsImpl(JSObject holder,InternalIndex entry)1509 static PropertyDetails GetDetailsImpl(JSObject holder, InternalIndex entry) {
1510 return GetDetailsImpl(holder.elements(), entry);
1511 }
1512
GetDetailsImpl(FixedArrayBase backing_store,InternalIndex entry)1513 static PropertyDetails GetDetailsImpl(FixedArrayBase backing_store,
1514 InternalIndex entry) {
1515 return NumberDictionary::cast(backing_store).DetailsAt(entry);
1516 }
1517
FilterKey(Handle<NumberDictionary> dictionary,InternalIndex entry,Object raw_key,PropertyFilter filter)1518 static uint32_t FilterKey(Handle<NumberDictionary> dictionary,
1519 InternalIndex entry, Object raw_key,
1520 PropertyFilter filter) {
1521 DCHECK(raw_key.IsNumber());
1522 DCHECK_LE(raw_key.Number(), kMaxUInt32);
1523 PropertyDetails details = dictionary->DetailsAt(entry);
1524 PropertyAttributes attr = details.attributes();
1525 if ((attr & filter) != 0) return kMaxUInt32;
1526 return static_cast<uint32_t>(raw_key.Number());
1527 }
1528
GetKeyForEntryImpl(Isolate * isolate,Handle<NumberDictionary> dictionary,InternalIndex entry,PropertyFilter filter)1529 static uint32_t GetKeyForEntryImpl(Isolate* isolate,
1530 Handle<NumberDictionary> dictionary,
1531 InternalIndex entry,
1532 PropertyFilter filter) {
1533 DisallowHeapAllocation no_gc;
1534 Object raw_key = dictionary->KeyAt(entry);
1535 if (!dictionary->IsKey(ReadOnlyRoots(isolate), raw_key)) return kMaxUInt32;
1536 return FilterKey(dictionary, entry, raw_key, filter);
1537 }
1538
CollectElementIndicesImpl(Handle<JSObject> object,Handle<FixedArrayBase> backing_store,KeyAccumulator * keys)1539 V8_WARN_UNUSED_RESULT static ExceptionStatus CollectElementIndicesImpl(
1540 Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
1541 KeyAccumulator* keys) {
1542 if (keys->filter() & SKIP_STRINGS) return ExceptionStatus::kSuccess;
1543 Isolate* isolate = keys->isolate();
1544 Handle<NumberDictionary> dictionary =
1545 Handle<NumberDictionary>::cast(backing_store);
1546 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(
1547 GetMaxNumberOfEntries(*object, *backing_store));
1548 int insertion_index = 0;
1549 PropertyFilter filter = keys->filter();
1550 ReadOnlyRoots roots(isolate);
1551 for (InternalIndex i : dictionary->IterateEntries()) {
1552 AllowHeapAllocation allow_gc;
1553 Object raw_key = dictionary->KeyAt(i);
1554 if (!dictionary->IsKey(roots, raw_key)) continue;
1555 uint32_t key = FilterKey(dictionary, i, raw_key, filter);
1556 if (key == kMaxUInt32) {
1557 // This might allocate, but {raw_key} is not used afterwards.
1558 keys->AddShadowingKey(raw_key, &allow_gc);
1559 continue;
1560 }
1561 elements->set(insertion_index, raw_key);
1562 insertion_index++;
1563 }
1564 SortIndices(isolate, elements, insertion_index);
1565 for (int i = 0; i < insertion_index; i++) {
1566 RETURN_FAILURE_IF_NOT_SUCCESSFUL(keys->AddKey(elements->get(i)));
1567 }
1568 return ExceptionStatus::kSuccess;
1569 }
1570
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)1571 static Handle<FixedArray> DirectCollectElementIndicesImpl(
1572 Isolate* isolate, Handle<JSObject> object,
1573 Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
1574 PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices,
1575 uint32_t insertion_index = 0) {
1576 if (filter & SKIP_STRINGS) return list;
1577 if (filter & ONLY_ALL_CAN_READ) return list;
1578
1579 Handle<NumberDictionary> dictionary =
1580 Handle<NumberDictionary>::cast(backing_store);
1581 for (InternalIndex i : dictionary->IterateEntries()) {
1582 uint32_t key = GetKeyForEntryImpl(isolate, dictionary, i, filter);
1583 if (key == kMaxUInt32) continue;
1584 Handle<Object> index = isolate->factory()->NewNumberFromUint(key);
1585 list->set(insertion_index, *index);
1586 insertion_index++;
1587 }
1588 *nof_indices = insertion_index;
1589 return list;
1590 }
1591
AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,KeyAccumulator * accumulator,AddKeyConversion convert)1592 V8_WARN_UNUSED_RESULT static ExceptionStatus AddElementsToKeyAccumulatorImpl(
1593 Handle<JSObject> receiver, KeyAccumulator* accumulator,
1594 AddKeyConversion convert) {
1595 Isolate* isolate = accumulator->isolate();
1596 Handle<NumberDictionary> dictionary(
1597 NumberDictionary::cast(receiver->elements()), isolate);
1598 ReadOnlyRoots roots(isolate);
1599 for (InternalIndex i : dictionary->IterateEntries()) {
1600 Object k = dictionary->KeyAt(i);
1601 if (!dictionary->IsKey(roots, k)) continue;
1602 Object value = dictionary->ValueAt(i);
1603 DCHECK(!value.IsTheHole(isolate));
1604 DCHECK(!value.IsAccessorPair());
1605 DCHECK(!value.IsAccessorInfo());
1606 RETURN_FAILURE_IF_NOT_SUCCESSFUL(accumulator->AddKey(value, convert));
1607 }
1608 return ExceptionStatus::kSuccess;
1609 }
1610
IncludesValueFastPath(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length,Maybe<bool> * result)1611 static bool IncludesValueFastPath(Isolate* isolate, Handle<JSObject> receiver,
1612 Handle<Object> value, size_t start_from,
1613 size_t length, Maybe<bool>* result) {
1614 DisallowHeapAllocation no_gc;
1615 NumberDictionary dictionary = NumberDictionary::cast(receiver->elements());
1616 Object the_hole = ReadOnlyRoots(isolate).the_hole_value();
1617 Object undefined = ReadOnlyRoots(isolate).undefined_value();
1618
1619 // Scan for accessor properties. If accessors are present, then elements
1620 // must be accessed in order via the slow path.
1621 bool found = false;
1622 for (InternalIndex i : dictionary.IterateEntries()) {
1623 Object k = dictionary.KeyAt(i);
1624 if (k == the_hole) continue;
1625 if (k == undefined) continue;
1626
1627 uint32_t index;
1628 if (!k.ToArrayIndex(&index) || index < start_from || index >= length) {
1629 continue;
1630 }
1631
1632 if (dictionary.DetailsAt(i).kind() == kAccessor) {
1633 // Restart from beginning in slow path, otherwise we may observably
1634 // access getters out of order
1635 return false;
1636 } else if (!found) {
1637 Object element_k = dictionary.ValueAt(i);
1638 if (value->SameValueZero(element_k)) found = true;
1639 }
1640 }
1641
1642 *result = Just(found);
1643 return true;
1644 }
1645
IncludesValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)1646 static Maybe<bool> IncludesValueImpl(Isolate* isolate,
1647 Handle<JSObject> receiver,
1648 Handle<Object> value, size_t start_from,
1649 size_t length) {
1650 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
1651 bool search_for_hole = value->IsUndefined(isolate);
1652
1653 if (!search_for_hole) {
1654 Maybe<bool> result = Nothing<bool>();
1655 if (DictionaryElementsAccessor::IncludesValueFastPath(
1656 isolate, receiver, value, start_from, length, &result)) {
1657 return result;
1658 }
1659 }
1660 ElementsKind original_elements_kind = receiver->GetElementsKind();
1661 USE(original_elements_kind);
1662 Handle<NumberDictionary> dictionary(
1663 NumberDictionary::cast(receiver->elements()), isolate);
1664 // Iterate through the entire range, as accessing elements out of order is
1665 // observable.
1666 for (size_t k = start_from; k < length; ++k) {
1667 DCHECK_EQ(receiver->GetElementsKind(), original_elements_kind);
1668 InternalIndex entry =
1669 dictionary->FindEntry(isolate, static_cast<uint32_t>(k));
1670 if (entry.is_not_found()) {
1671 if (search_for_hole) return Just(true);
1672 continue;
1673 }
1674
1675 PropertyDetails details = GetDetailsImpl(*dictionary, entry);
1676 switch (details.kind()) {
1677 case kData: {
1678 Object element_k = dictionary->ValueAt(entry);
1679 if (value->SameValueZero(element_k)) return Just(true);
1680 break;
1681 }
1682 case kAccessor: {
1683 LookupIterator it(isolate, receiver, k,
1684 LookupIterator::OWN_SKIP_INTERCEPTOR);
1685 DCHECK(it.IsFound());
1686 DCHECK_EQ(it.state(), LookupIterator::ACCESSOR);
1687 Handle<Object> element_k;
1688
1689 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k,
1690 Object::GetPropertyWithAccessor(&it),
1691 Nothing<bool>());
1692
1693 if (value->SameValueZero(*element_k)) return Just(true);
1694
1695 // Bailout to slow path if elements on prototype changed
1696 if (!JSObject::PrototypeHasNoElements(isolate, *receiver)) {
1697 return IncludesValueSlowPath(isolate, receiver, value, k + 1,
1698 length);
1699 }
1700
1701 // Continue if elements unchanged
1702 if (*dictionary == receiver->elements()) continue;
1703
1704 // Otherwise, bailout or update elements
1705
1706 // If switched to initial elements, return true if searching for
1707 // undefined, and false otherwise.
1708 if (receiver->map().GetInitialElements() == receiver->elements()) {
1709 return Just(search_for_hole);
1710 }
1711
1712 // If switched to fast elements, continue with the correct accessor.
1713 if (receiver->GetElementsKind() != DICTIONARY_ELEMENTS) {
1714 ElementsAccessor* accessor = receiver->GetElementsAccessor();
1715 return accessor->IncludesValue(isolate, receiver, value, k + 1,
1716 length);
1717 }
1718 dictionary =
1719 handle(NumberDictionary::cast(receiver->elements()), isolate);
1720 break;
1721 }
1722 }
1723 }
1724 return Just(false);
1725 }
1726
IndexOfValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)1727 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
1728 Handle<JSObject> receiver,
1729 Handle<Object> value,
1730 size_t start_from, size_t length) {
1731 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
1732
1733 ElementsKind original_elements_kind = receiver->GetElementsKind();
1734 USE(original_elements_kind);
1735 Handle<NumberDictionary> dictionary(
1736 NumberDictionary::cast(receiver->elements()), isolate);
1737 // Iterate through entire range, as accessing elements out of order is
1738 // observable.
1739 for (size_t k = start_from; k < length; ++k) {
1740 DCHECK_EQ(receiver->GetElementsKind(), original_elements_kind);
1741 DCHECK_LE(k, std::numeric_limits<uint32_t>::max());
1742 InternalIndex entry =
1743 dictionary->FindEntry(isolate, static_cast<uint32_t>(k));
1744 if (entry.is_not_found()) continue;
1745
1746 PropertyDetails details =
1747 GetDetailsImpl(*dictionary, InternalIndex(entry));
1748 switch (details.kind()) {
1749 case kData: {
1750 Object element_k = dictionary->ValueAt(entry);
1751 if (value->StrictEquals(element_k)) {
1752 return Just<int64_t>(k);
1753 }
1754 break;
1755 }
1756 case kAccessor: {
1757 LookupIterator it(isolate, receiver, k,
1758 LookupIterator::OWN_SKIP_INTERCEPTOR);
1759 DCHECK(it.IsFound());
1760 DCHECK_EQ(it.state(), LookupIterator::ACCESSOR);
1761 Handle<Object> element_k;
1762
1763 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k,
1764 Object::GetPropertyWithAccessor(&it),
1765 Nothing<int64_t>());
1766
1767 if (value->StrictEquals(*element_k)) return Just<int64_t>(k);
1768
1769 // Bailout to slow path if elements on prototype changed.
1770 if (!JSObject::PrototypeHasNoElements(isolate, *receiver)) {
1771 return IndexOfValueSlowPath(isolate, receiver, value, k + 1,
1772 length);
1773 }
1774
1775 // Continue if elements unchanged.
1776 if (*dictionary == receiver->elements()) continue;
1777
1778 // Otherwise, bailout or update elements.
1779 if (receiver->GetElementsKind() != DICTIONARY_ELEMENTS) {
1780 // Otherwise, switch to slow path.
1781 return IndexOfValueSlowPath(isolate, receiver, value, k + 1,
1782 length);
1783 }
1784 dictionary =
1785 handle(NumberDictionary::cast(receiver->elements()), isolate);
1786 break;
1787 }
1788 }
1789 }
1790 return Just<int64_t>(-1);
1791 }
1792
ValidateContents(JSObject holder,size_t length)1793 static void ValidateContents(JSObject holder, size_t length) {
1794 DisallowHeapAllocation no_gc;
1795 #if DEBUG
1796 DCHECK_EQ(holder.map().elements_kind(), DICTIONARY_ELEMENTS);
1797 if (!FLAG_enable_slow_asserts) return;
1798 ReadOnlyRoots roots = holder.GetReadOnlyRoots();
1799 NumberDictionary dictionary = NumberDictionary::cast(holder.elements());
1800 // Validate the requires_slow_elements and max_number_key values.
1801 bool requires_slow_elements = false;
1802 int max_key = 0;
1803 for (InternalIndex i : dictionary.IterateEntries()) {
1804 Object k;
1805 if (!dictionary.ToKey(roots, i, &k)) continue;
1806 DCHECK_LE(0.0, k.Number());
1807 if (k.Number() > NumberDictionary::kRequiresSlowElementsLimit) {
1808 requires_slow_elements = true;
1809 } else {
1810 max_key = Max(max_key, Smi::ToInt(k));
1811 }
1812 }
1813 if (requires_slow_elements) {
1814 DCHECK(dictionary.requires_slow_elements());
1815 } else if (!dictionary.requires_slow_elements()) {
1816 DCHECK_LE(max_key, dictionary.max_number_key());
1817 }
1818 #endif
1819 }
1820 };
1821
1822 // Super class for all fast element arrays.
1823 template <typename Subclass, typename KindTraits>
1824 class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
1825 public:
1826 using BackingStore = typename KindTraits::BackingStore;
1827
NormalizeImpl(Handle<JSObject> object,Handle<FixedArrayBase> store)1828 static Handle<NumberDictionary> NormalizeImpl(Handle<JSObject> object,
1829 Handle<FixedArrayBase> store) {
1830 Isolate* isolate = object->GetIsolate();
1831 ElementsKind kind = Subclass::kind();
1832
1833 // Ensure that notifications fire if the array or object prototypes are
1834 // normalizing.
1835 if (IsSmiOrObjectElementsKind(kind) ||
1836 kind == FAST_STRING_WRAPPER_ELEMENTS) {
1837 isolate->UpdateNoElementsProtectorOnNormalizeElements(object);
1838 }
1839
1840 int capacity = object->GetFastElementsUsage();
1841 Handle<NumberDictionary> dictionary =
1842 NumberDictionary::New(isolate, capacity);
1843
1844 PropertyDetails details = PropertyDetails::Empty();
1845 int j = 0;
1846 int max_number_key = -1;
1847 for (int i = 0; j < capacity; i++) {
1848 if (IsHoleyElementsKindForRead(kind)) {
1849 if (BackingStore::cast(*store).is_the_hole(isolate, i)) continue;
1850 }
1851 max_number_key = i;
1852 Handle<Object> value =
1853 Subclass::GetImpl(isolate, *store, InternalIndex(i));
1854 dictionary =
1855 NumberDictionary::Add(isolate, dictionary, i, value, details);
1856 j++;
1857 }
1858
1859 if (max_number_key > 0) {
1860 dictionary->UpdateMaxNumberKey(static_cast<uint32_t>(max_number_key),
1861 object);
1862 }
1863 return dictionary;
1864 }
1865
DeleteAtEnd(Handle<JSObject> obj,Handle<BackingStore> backing_store,uint32_t entry)1866 static void DeleteAtEnd(Handle<JSObject> obj,
1867 Handle<BackingStore> backing_store, uint32_t entry) {
1868 uint32_t length = static_cast<uint32_t>(backing_store->length());
1869 Isolate* isolate = obj->GetIsolate();
1870 for (; entry > 0; entry--) {
1871 if (!backing_store->is_the_hole(isolate, entry - 1)) break;
1872 }
1873 if (entry == 0) {
1874 FixedArray empty = ReadOnlyRoots(isolate).empty_fixed_array();
1875 // Dynamically ask for the elements kind here since we manually redirect
1876 // the operations for argument backing stores.
1877 if (obj->GetElementsKind() == FAST_SLOPPY_ARGUMENTS_ELEMENTS) {
1878 SloppyArgumentsElements::cast(obj->elements()).set_arguments(empty);
1879 } else {
1880 obj->set_elements(empty);
1881 }
1882 return;
1883 }
1884
1885 isolate->heap()->RightTrimFixedArray(*backing_store, length - entry);
1886 }
1887
DeleteCommon(Handle<JSObject> obj,uint32_t entry,Handle<FixedArrayBase> store)1888 static void DeleteCommon(Handle<JSObject> obj, uint32_t entry,
1889 Handle<FixedArrayBase> store) {
1890 DCHECK(obj->HasSmiOrObjectElements() || obj->HasDoubleElements() ||
1891 obj->HasNonextensibleElements() || obj->HasFastArgumentsElements() ||
1892 obj->HasFastStringWrapperElements());
1893 Handle<BackingStore> backing_store = Handle<BackingStore>::cast(store);
1894 if (!obj->IsJSArray() &&
1895 entry == static_cast<uint32_t>(store->length()) - 1) {
1896 DeleteAtEnd(obj, backing_store, entry);
1897 return;
1898 }
1899
1900 Isolate* isolate = obj->GetIsolate();
1901 backing_store->set_the_hole(isolate, entry);
1902
1903 // TODO(verwaest): Move this out of elements.cc.
1904 // If an old space backing store is larger than a certain size and
1905 // has too few used values, normalize it.
1906 const int kMinLengthForSparsenessCheck = 64;
1907 if (backing_store->length() < kMinLengthForSparsenessCheck) return;
1908 // TODO(ulan): Check if it works with young large objects.
1909 if (ObjectInYoungGeneration(*backing_store)) return;
1910 uint32_t length = 0;
1911 if (obj->IsJSArray()) {
1912 JSArray::cast(*obj).length().ToArrayLength(&length);
1913 } else {
1914 length = static_cast<uint32_t>(store->length());
1915 }
1916
1917 // To avoid doing the check on every delete, use a counter-based heuristic.
1918 const int kLengthFraction = 16;
1919 // The above constant must be large enough to ensure that we check for
1920 // normalization frequently enough. At a minimum, it should be large
1921 // enough to reliably hit the "window" of remaining elements count where
1922 // normalization would be beneficial.
1923 STATIC_ASSERT(kLengthFraction >=
1924 NumberDictionary::kEntrySize *
1925 NumberDictionary::kPreferFastElementsSizeFactor);
1926 size_t current_counter = isolate->elements_deletion_counter();
1927 if (current_counter < length / kLengthFraction) {
1928 isolate->set_elements_deletion_counter(current_counter + 1);
1929 return;
1930 }
1931 // Reset the counter whenever the full check is performed.
1932 isolate->set_elements_deletion_counter(0);
1933
1934 if (!obj->IsJSArray()) {
1935 uint32_t i;
1936 for (i = entry + 1; i < length; i++) {
1937 if (!backing_store->is_the_hole(isolate, i)) break;
1938 }
1939 if (i == length) {
1940 DeleteAtEnd(obj, backing_store, entry);
1941 return;
1942 }
1943 }
1944 int num_used = 0;
1945 for (int i = 0; i < backing_store->length(); ++i) {
1946 if (!backing_store->is_the_hole(isolate, i)) {
1947 ++num_used;
1948 // Bail out if a number dictionary wouldn't be able to save much space.
1949 if (NumberDictionary::kPreferFastElementsSizeFactor *
1950 NumberDictionary::ComputeCapacity(num_used) *
1951 NumberDictionary::kEntrySize >
1952 static_cast<uint32_t>(backing_store->length())) {
1953 return;
1954 }
1955 }
1956 }
1957 JSObject::NormalizeElements(obj);
1958 }
1959
ReconfigureImpl(Handle<JSObject> object,Handle<FixedArrayBase> store,InternalIndex entry,Handle<Object> value,PropertyAttributes attributes)1960 static void ReconfigureImpl(Handle<JSObject> object,
1961 Handle<FixedArrayBase> store, InternalIndex entry,
1962 Handle<Object> value,
1963 PropertyAttributes attributes) {
1964 Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(object);
1965 entry = InternalIndex(
1966 dictionary->FindEntry(object->GetIsolate(), entry.as_uint32()));
1967 DictionaryElementsAccessor::ReconfigureImpl(object, dictionary, entry,
1968 value, attributes);
1969 }
1970
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)1971 static void AddImpl(Handle<JSObject> object, uint32_t index,
1972 Handle<Object> value, PropertyAttributes attributes,
1973 uint32_t new_capacity) {
1974 DCHECK_EQ(NONE, attributes);
1975 ElementsKind from_kind = object->GetElementsKind();
1976 ElementsKind to_kind = Subclass::kind();
1977 if (IsDictionaryElementsKind(from_kind) ||
1978 IsDoubleElementsKind(from_kind) != IsDoubleElementsKind(to_kind) ||
1979 Subclass::GetCapacityImpl(*object, object->elements()) !=
1980 new_capacity) {
1981 Subclass::GrowCapacityAndConvertImpl(object, new_capacity);
1982 } else {
1983 if (IsFastElementsKind(from_kind) && from_kind != to_kind) {
1984 JSObject::TransitionElementsKind(object, to_kind);
1985 }
1986 if (IsSmiOrObjectElementsKind(from_kind)) {
1987 DCHECK(IsSmiOrObjectElementsKind(to_kind));
1988 JSObject::EnsureWritableFastElements(object);
1989 }
1990 }
1991 Subclass::SetImpl(object, InternalIndex(index), *value);
1992 }
1993
DeleteImpl(Handle<JSObject> obj,InternalIndex entry)1994 static void DeleteImpl(Handle<JSObject> obj, InternalIndex entry) {
1995 ElementsKind kind = KindTraits::Kind;
1996 if (IsFastPackedElementsKind(kind) ||
1997 kind == PACKED_NONEXTENSIBLE_ELEMENTS) {
1998 JSObject::TransitionElementsKind(obj, GetHoleyElementsKind(kind));
1999 }
2000 if (IsSmiOrObjectElementsKind(KindTraits::Kind) ||
2001 IsNonextensibleElementsKind(kind)) {
2002 JSObject::EnsureWritableFastElements(obj);
2003 }
2004 DeleteCommon(obj, entry.as_uint32(),
2005 handle(obj->elements(), obj->GetIsolate()));
2006 }
2007
HasEntryImpl(Isolate * isolate,FixedArrayBase backing_store,InternalIndex entry)2008 static bool HasEntryImpl(Isolate* isolate, FixedArrayBase backing_store,
2009 InternalIndex entry) {
2010 return !BackingStore::cast(backing_store)
2011 .is_the_hole(isolate, entry.as_int());
2012 }
2013
NumberOfElementsImpl(JSObject receiver,FixedArrayBase backing_store)2014 static uint32_t NumberOfElementsImpl(JSObject receiver,
2015 FixedArrayBase backing_store) {
2016 size_t max_index = Subclass::GetMaxIndex(receiver, backing_store);
2017 DCHECK_LE(max_index, std::numeric_limits<uint32_t>::max());
2018 if (IsFastPackedElementsKind(Subclass::kind())) {
2019 return static_cast<uint32_t>(max_index);
2020 }
2021 Isolate* isolate = receiver.GetIsolate();
2022 uint32_t count = 0;
2023 for (size_t i = 0; i < max_index; i++) {
2024 if (Subclass::HasEntryImpl(isolate, backing_store, InternalIndex(i))) {
2025 count++;
2026 }
2027 }
2028 return count;
2029 }
2030
AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,KeyAccumulator * accumulator,AddKeyConversion convert)2031 V8_WARN_UNUSED_RESULT static ExceptionStatus AddElementsToKeyAccumulatorImpl(
2032 Handle<JSObject> receiver, KeyAccumulator* accumulator,
2033 AddKeyConversion convert) {
2034 Isolate* isolate = accumulator->isolate();
2035 Handle<FixedArrayBase> elements(receiver->elements(), isolate);
2036 size_t length = Subclass::GetMaxNumberOfEntries(*receiver, *elements);
2037 for (size_t i = 0; i < length; i++) {
2038 if (IsFastPackedElementsKind(KindTraits::Kind) ||
2039 HasEntryImpl(isolate, *elements, InternalIndex(i))) {
2040 RETURN_FAILURE_IF_NOT_SUCCESSFUL(accumulator->AddKey(
2041 Subclass::GetImpl(isolate, *elements, InternalIndex(i)), convert));
2042 }
2043 }
2044 return ExceptionStatus::kSuccess;
2045 }
2046
ValidateContents(JSObject holder,size_t length)2047 static void ValidateContents(JSObject holder, size_t length) {
2048 #if DEBUG
2049 Isolate* isolate = holder.GetIsolate();
2050 Heap* heap = isolate->heap();
2051 FixedArrayBase elements = holder.elements();
2052 Map map = elements.map();
2053 if (IsSmiOrObjectElementsKind(KindTraits::Kind)) {
2054 DCHECK_NE(map, ReadOnlyRoots(heap).fixed_double_array_map());
2055 } else if (IsDoubleElementsKind(KindTraits::Kind)) {
2056 DCHECK_NE(map, ReadOnlyRoots(heap).fixed_cow_array_map());
2057 if (map == ReadOnlyRoots(heap).fixed_array_map()) DCHECK_EQ(0u, length);
2058 } else {
2059 UNREACHABLE();
2060 }
2061 if (length == 0u) return; // nothing to do!
2062 #if ENABLE_SLOW_DCHECKS
2063 DisallowHeapAllocation no_gc;
2064 BackingStore backing_store = BackingStore::cast(elements);
2065 DCHECK(length <= std::numeric_limits<int>::max());
2066 int length_int = static_cast<int>(length);
2067 if (IsSmiElementsKind(KindTraits::Kind)) {
2068 HandleScope scope(isolate);
2069 for (int i = 0; i < length_int; i++) {
2070 DCHECK(BackingStore::get(backing_store, i, isolate)->IsSmi() ||
2071 (IsHoleyElementsKind(KindTraits::Kind) &&
2072 backing_store.is_the_hole(isolate, i)));
2073 }
2074 } else if (KindTraits::Kind == PACKED_ELEMENTS ||
2075 KindTraits::Kind == PACKED_DOUBLE_ELEMENTS) {
2076 for (int i = 0; i < length_int; i++) {
2077 DCHECK(!backing_store.is_the_hole(isolate, i));
2078 }
2079 } else {
2080 DCHECK(IsHoleyElementsKind(KindTraits::Kind));
2081 }
2082 #endif
2083 #endif
2084 }
2085
PopImpl(Handle<JSArray> receiver)2086 static Handle<Object> PopImpl(Handle<JSArray> receiver) {
2087 return Subclass::RemoveElement(receiver, AT_END);
2088 }
2089
ShiftImpl(Handle<JSArray> receiver)2090 static Handle<Object> ShiftImpl(Handle<JSArray> receiver) {
2091 return Subclass::RemoveElement(receiver, AT_START);
2092 }
2093
PushImpl(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t push_size)2094 static uint32_t PushImpl(Handle<JSArray> receiver, BuiltinArguments* args,
2095 uint32_t push_size) {
2096 Handle<FixedArrayBase> backing_store(receiver->elements(),
2097 receiver->GetIsolate());
2098 return Subclass::AddArguments(receiver, backing_store, args, push_size,
2099 AT_END);
2100 }
2101
UnshiftImpl(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t unshift_size)2102 static uint32_t UnshiftImpl(Handle<JSArray> receiver, BuiltinArguments* args,
2103 uint32_t unshift_size) {
2104 Handle<FixedArrayBase> backing_store(receiver->elements(),
2105 receiver->GetIsolate());
2106 return Subclass::AddArguments(receiver, backing_store, args, unshift_size,
2107 AT_START);
2108 }
2109
MoveElements(Isolate * isolate,Handle<JSArray> receiver,Handle<FixedArrayBase> backing_store,int dst_index,int src_index,int len,int hole_start,int hole_end)2110 static void MoveElements(Isolate* isolate, Handle<JSArray> receiver,
2111 Handle<FixedArrayBase> backing_store, int dst_index,
2112 int src_index, int len, int hole_start,
2113 int hole_end) {
2114 Handle<BackingStore> dst_elms = Handle<BackingStore>::cast(backing_store);
2115 if (len > JSArray::kMaxCopyElements && dst_index == 0 &&
2116 isolate->heap()->CanMoveObjectStart(*dst_elms)) {
2117 // Update all the copies of this backing_store handle.
2118 *dst_elms.location() =
2119 BackingStore::cast(
2120 isolate->heap()->LeftTrimFixedArray(*dst_elms, src_index))
2121 .ptr();
2122 receiver->set_elements(*dst_elms);
2123 // Adjust the hole offset as the array has been shrunk.
2124 hole_end -= src_index;
2125 DCHECK_LE(hole_start, backing_store->length());
2126 DCHECK_LE(hole_end, backing_store->length());
2127 } else if (len != 0) {
2128 WriteBarrierMode mode = GetWriteBarrierMode(KindTraits::Kind);
2129 dst_elms->MoveElements(isolate, dst_index, src_index, len, mode);
2130 }
2131 if (hole_start != hole_end) {
2132 dst_elms->FillWithHoles(hole_start, hole_end);
2133 }
2134 }
2135
FillImpl(Handle<JSObject> receiver,Handle<Object> obj_value,size_t start,size_t end)2136 static Object FillImpl(Handle<JSObject> receiver, Handle<Object> obj_value,
2137 size_t start, size_t end) {
2138 // Ensure indexes are within array bounds
2139 DCHECK_LE(0, start);
2140 DCHECK_LE(start, end);
2141
2142 // Make sure COW arrays are copied.
2143 if (IsSmiOrObjectElementsKind(Subclass::kind())) {
2144 JSObject::EnsureWritableFastElements(receiver);
2145 }
2146
2147 // Make sure we have enough space.
2148 DCHECK_LE(end, std::numeric_limits<uint32_t>::max());
2149 if (end > Subclass::GetCapacityImpl(*receiver, receiver->elements())) {
2150 Subclass::GrowCapacityAndConvertImpl(receiver,
2151 static_cast<uint32_t>(end));
2152 CHECK_EQ(Subclass::kind(), receiver->GetElementsKind());
2153 }
2154 DCHECK_LE(end, Subclass::GetCapacityImpl(*receiver, receiver->elements()));
2155
2156 for (size_t index = start; index < end; ++index) {
2157 Subclass::SetImpl(receiver, InternalIndex(index), *obj_value);
2158 }
2159 return *receiver;
2160 }
2161
IncludesValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> search_value,size_t start_from,size_t length)2162 static Maybe<bool> IncludesValueImpl(Isolate* isolate,
2163 Handle<JSObject> receiver,
2164 Handle<Object> search_value,
2165 size_t start_from, size_t length) {
2166 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
2167 DisallowHeapAllocation no_gc;
2168 FixedArrayBase elements_base = receiver->elements();
2169 Object the_hole = ReadOnlyRoots(isolate).the_hole_value();
2170 Object undefined = ReadOnlyRoots(isolate).undefined_value();
2171 Object value = *search_value;
2172
2173 if (start_from >= length) return Just(false);
2174
2175 // Elements beyond the capacity of the backing store treated as undefined.
2176 size_t elements_length = static_cast<size_t>(elements_base.length());
2177 if (value == undefined && elements_length < length) return Just(true);
2178 if (elements_length == 0) {
2179 DCHECK_NE(value, undefined);
2180 return Just(false);
2181 }
2182
2183 length = std::min(elements_length, length);
2184 DCHECK_LE(length, std::numeric_limits<int>::max());
2185
2186 if (!value.IsNumber()) {
2187 if (value == undefined) {
2188 // Search for `undefined` or The Hole. Even in the case of
2189 // PACKED_DOUBLE_ELEMENTS or PACKED_SMI_ELEMENTS, we might encounter The
2190 // Hole here, since the {length} used here can be larger than
2191 // JSArray::length.
2192 if (IsSmiOrObjectElementsKind(Subclass::kind()) ||
2193 IsAnyNonextensibleElementsKind(Subclass::kind())) {
2194 FixedArray elements = FixedArray::cast(receiver->elements());
2195
2196 for (size_t k = start_from; k < length; ++k) {
2197 Object element_k = elements.get(static_cast<int>(k));
2198
2199 if (element_k == the_hole || element_k == undefined) {
2200 return Just(true);
2201 }
2202 }
2203 return Just(false);
2204 } else {
2205 // Search for The Hole in HOLEY_DOUBLE_ELEMENTS or
2206 // PACKED_DOUBLE_ELEMENTS.
2207 DCHECK(IsDoubleElementsKind(Subclass::kind()));
2208 FixedDoubleArray elements =
2209 FixedDoubleArray::cast(receiver->elements());
2210
2211 for (size_t k = start_from; k < length; ++k) {
2212 if (elements.is_the_hole(static_cast<int>(k))) return Just(true);
2213 }
2214 return Just(false);
2215 }
2216 } else if (!IsObjectElementsKind(Subclass::kind()) &&
2217 !IsAnyNonextensibleElementsKind(Subclass::kind())) {
2218 // Search for non-number, non-Undefined value, with either
2219 // PACKED_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, HOLEY_SMI_ELEMENTS or
2220 // HOLEY_DOUBLE_ELEMENTS. Guaranteed to return false, since these
2221 // elements kinds can only contain Number values or undefined.
2222 return Just(false);
2223 } else {
2224 // Search for non-number, non-Undefined value with either
2225 // PACKED_ELEMENTS or HOLEY_ELEMENTS.
2226 DCHECK(IsObjectElementsKind(Subclass::kind()) ||
2227 IsAnyNonextensibleElementsKind(Subclass::kind()));
2228 FixedArray elements = FixedArray::cast(receiver->elements());
2229
2230 for (size_t k = start_from; k < length; ++k) {
2231 Object element_k = elements.get(static_cast<int>(k));
2232 if (element_k == the_hole) continue;
2233 if (value.SameValueZero(element_k)) return Just(true);
2234 }
2235 return Just(false);
2236 }
2237 } else {
2238 if (!value.IsNaN()) {
2239 double search_value = value.Number();
2240 if (IsDoubleElementsKind(Subclass::kind())) {
2241 // Search for non-NaN Number in PACKED_DOUBLE_ELEMENTS or
2242 // HOLEY_DOUBLE_ELEMENTS --- Skip TheHole, and trust UCOMISD or
2243 // similar operation for result.
2244 FixedDoubleArray elements =
2245 FixedDoubleArray::cast(receiver->elements());
2246
2247 for (size_t k = start_from; k < length; ++k) {
2248 if (elements.is_the_hole(static_cast<int>(k))) continue;
2249 if (elements.get_scalar(static_cast<int>(k)) == search_value) {
2250 return Just(true);
2251 }
2252 }
2253 return Just(false);
2254 } else {
2255 // Search for non-NaN Number in PACKED_ELEMENTS, HOLEY_ELEMENTS,
2256 // PACKED_SMI_ELEMENTS or HOLEY_SMI_ELEMENTS --- Skip non-Numbers,
2257 // and trust UCOMISD or similar operation for result
2258 FixedArray elements = FixedArray::cast(receiver->elements());
2259
2260 for (size_t k = start_from; k < length; ++k) {
2261 Object element_k = elements.get(static_cast<int>(k));
2262 if (element_k.IsNumber() && element_k.Number() == search_value) {
2263 return Just(true);
2264 }
2265 }
2266 return Just(false);
2267 }
2268 } else {
2269 // Search for NaN --- NaN cannot be represented with Smi elements, so
2270 // abort if ElementsKind is PACKED_SMI_ELEMENTS or HOLEY_SMI_ELEMENTS
2271 if (IsSmiElementsKind(Subclass::kind())) return Just(false);
2272
2273 if (IsDoubleElementsKind(Subclass::kind())) {
2274 // Search for NaN in PACKED_DOUBLE_ELEMENTS or
2275 // HOLEY_DOUBLE_ELEMENTS --- Skip The Hole and trust
2276 // std::isnan(elementK) for result
2277 FixedDoubleArray elements =
2278 FixedDoubleArray::cast(receiver->elements());
2279
2280 for (size_t k = start_from; k < length; ++k) {
2281 if (elements.is_the_hole(static_cast<int>(k))) continue;
2282 if (std::isnan(elements.get_scalar(static_cast<int>(k)))) {
2283 return Just(true);
2284 }
2285 }
2286 return Just(false);
2287 } else {
2288 // Search for NaN in PACKED_ELEMENTS or HOLEY_ELEMENTS. Return true
2289 // if elementK->IsHeapNumber() && std::isnan(elementK->Number())
2290 DCHECK(IsObjectElementsKind(Subclass::kind()) ||
2291 IsAnyNonextensibleElementsKind(Subclass::kind()));
2292 FixedArray elements = FixedArray::cast(receiver->elements());
2293
2294 for (size_t k = start_from; k < length; ++k) {
2295 if (elements.get(static_cast<int>(k)).IsNaN()) return Just(true);
2296 }
2297 return Just(false);
2298 }
2299 }
2300 }
2301 }
2302
CreateListFromArrayLikeImpl(Isolate * isolate,Handle<JSObject> object,uint32_t length)2303 static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
2304 Handle<JSObject> object,
2305 uint32_t length) {
2306 Handle<FixedArray> result = isolate->factory()->NewFixedArray(length);
2307 Handle<FixedArrayBase> elements(object->elements(), isolate);
2308 for (uint32_t i = 0; i < length; i++) {
2309 InternalIndex entry(i);
2310 if (!Subclass::HasEntryImpl(isolate, *elements, entry)) continue;
2311 Handle<Object> value;
2312 value = Subclass::GetImpl(isolate, *elements, entry);
2313 if (value->IsName()) {
2314 value = isolate->factory()->InternalizeName(Handle<Name>::cast(value));
2315 }
2316 result->set(i, *value);
2317 }
2318 return result;
2319 }
2320
RemoveElement(Handle<JSArray> receiver,Where remove_position)2321 static Handle<Object> RemoveElement(Handle<JSArray> receiver,
2322 Where remove_position) {
2323 Isolate* isolate = receiver->GetIsolate();
2324 ElementsKind kind = KindTraits::Kind;
2325 if (IsSmiOrObjectElementsKind(kind)) {
2326 HandleScope scope(isolate);
2327 JSObject::EnsureWritableFastElements(receiver);
2328 }
2329 Handle<FixedArrayBase> backing_store(receiver->elements(), isolate);
2330 uint32_t length = static_cast<uint32_t>(Smi::ToInt(receiver->length()));
2331 DCHECK_GT(length, 0);
2332 int new_length = length - 1;
2333 int remove_index = remove_position == AT_START ? 0 : new_length;
2334 Handle<Object> result =
2335 Subclass::GetImpl(isolate, *backing_store, InternalIndex(remove_index));
2336 if (remove_position == AT_START) {
2337 Subclass::MoveElements(isolate, receiver, backing_store, 0, 1, new_length,
2338 0, 0);
2339 }
2340 Subclass::SetLengthImpl(isolate, receiver, new_length, backing_store);
2341
2342 if (IsHoleyElementsKind(kind) && result->IsTheHole(isolate)) {
2343 return isolate->factory()->undefined_value();
2344 }
2345 return result;
2346 }
2347
AddArguments(Handle<JSArray> receiver,Handle<FixedArrayBase> backing_store,BuiltinArguments * args,uint32_t add_size,Where add_position)2348 static uint32_t AddArguments(Handle<JSArray> receiver,
2349 Handle<FixedArrayBase> backing_store,
2350 BuiltinArguments* args, uint32_t add_size,
2351 Where add_position) {
2352 uint32_t length = Smi::ToInt(receiver->length());
2353 DCHECK_LT(0, add_size);
2354 uint32_t elms_len = backing_store->length();
2355 // Check we do not overflow the new_length.
2356 DCHECK(add_size <= static_cast<uint32_t>(Smi::kMaxValue - length));
2357 uint32_t new_length = length + add_size;
2358
2359 if (new_length > elms_len) {
2360 // New backing storage is needed.
2361 uint32_t capacity = JSObject::NewElementsCapacity(new_length);
2362 // If we add arguments to the start we have to shift the existing objects.
2363 int copy_dst_index = add_position == AT_START ? add_size : 0;
2364 // Copy over all objects to a new backing_store.
2365 backing_store = Subclass::ConvertElementsWithCapacity(
2366 receiver, backing_store, KindTraits::Kind, capacity, 0,
2367 copy_dst_index);
2368 receiver->set_elements(*backing_store);
2369 } else if (add_position == AT_START) {
2370 // If the backing store has enough capacity and we add elements to the
2371 // start we have to shift the existing objects.
2372 Isolate* isolate = receiver->GetIsolate();
2373 Subclass::MoveElements(isolate, receiver, backing_store, add_size, 0,
2374 length, 0, 0);
2375 }
2376
2377 int insertion_index = add_position == AT_START ? 0 : length;
2378 // Copy the arguments to the start.
2379 Subclass::CopyArguments(args, backing_store, add_size, 1, insertion_index);
2380 // Set the length.
2381 receiver->set_length(Smi::FromInt(new_length));
2382 return new_length;
2383 }
2384
CopyArguments(BuiltinArguments * args,Handle<FixedArrayBase> dst_store,uint32_t copy_size,uint32_t src_index,uint32_t dst_index)2385 static void CopyArguments(BuiltinArguments* args,
2386 Handle<FixedArrayBase> dst_store,
2387 uint32_t copy_size, uint32_t src_index,
2388 uint32_t dst_index) {
2389 // Add the provided values.
2390 DisallowHeapAllocation no_gc;
2391 FixedArrayBase raw_backing_store = *dst_store;
2392 WriteBarrierMode mode = raw_backing_store.GetWriteBarrierMode(no_gc);
2393 for (uint32_t i = 0; i < copy_size; i++) {
2394 Object argument = (*args)[src_index + i];
2395 DCHECK(!argument.IsTheHole());
2396 Subclass::SetImpl(raw_backing_store, InternalIndex(dst_index + i),
2397 argument, mode);
2398 }
2399 }
2400 };
2401
2402 template <typename Subclass, typename KindTraits>
2403 class FastSmiOrObjectElementsAccessor
2404 : public FastElementsAccessor<Subclass, KindTraits> {
2405 public:
SetImpl(Handle<JSObject> holder,InternalIndex entry,Object value)2406 static inline void SetImpl(Handle<JSObject> holder, InternalIndex entry,
2407 Object value) {
2408 SetImpl(holder->elements(), entry, value);
2409 }
2410
SetImpl(FixedArrayBase backing_store,InternalIndex entry,Object value)2411 static inline void SetImpl(FixedArrayBase backing_store, InternalIndex entry,
2412 Object value) {
2413 FixedArray::cast(backing_store).set(entry.as_int(), value);
2414 }
2415
SetImpl(FixedArrayBase backing_store,InternalIndex entry,Object value,WriteBarrierMode mode)2416 static inline void SetImpl(FixedArrayBase backing_store, InternalIndex entry,
2417 Object value, WriteBarrierMode mode) {
2418 FixedArray::cast(backing_store).set(entry.as_int(), value, mode);
2419 }
2420
GetRaw(FixedArray backing_store,InternalIndex entry)2421 static Object GetRaw(FixedArray backing_store, InternalIndex entry) {
2422 return backing_store.get(entry.as_int());
2423 }
2424
2425 // NOTE: this method violates the handlified function signature convention:
2426 // raw pointer parameters in the function that allocates.
2427 // See ElementsAccessor::CopyElements() for details.
2428 // This method could actually allocate if copying from double elements to
2429 // object elements.
CopyElementsImpl(Isolate * isolate,FixedArrayBase from,uint32_t from_start,FixedArrayBase to,ElementsKind from_kind,uint32_t to_start,int packed_size,int copy_size)2430 static void CopyElementsImpl(Isolate* isolate, FixedArrayBase from,
2431 uint32_t from_start, FixedArrayBase to,
2432 ElementsKind from_kind, uint32_t to_start,
2433 int packed_size, int copy_size) {
2434 DisallowHeapAllocation no_gc;
2435 ElementsKind to_kind = KindTraits::Kind;
2436 switch (from_kind) {
2437 case PACKED_SMI_ELEMENTS:
2438 case HOLEY_SMI_ELEMENTS:
2439 case PACKED_ELEMENTS:
2440 case PACKED_FROZEN_ELEMENTS:
2441 case PACKED_SEALED_ELEMENTS:
2442 case PACKED_NONEXTENSIBLE_ELEMENTS:
2443 case HOLEY_ELEMENTS:
2444 case HOLEY_FROZEN_ELEMENTS:
2445 case HOLEY_SEALED_ELEMENTS:
2446 case HOLEY_NONEXTENSIBLE_ELEMENTS:
2447 CopyObjectToObjectElements(isolate, from, from_kind, from_start, to,
2448 to_kind, to_start, copy_size);
2449 break;
2450 case PACKED_DOUBLE_ELEMENTS:
2451 case HOLEY_DOUBLE_ELEMENTS: {
2452 AllowHeapAllocation allow_allocation;
2453 DCHECK(IsObjectElementsKind(to_kind));
2454 CopyDoubleToObjectElements(isolate, from, from_start, to, to_start,
2455 copy_size);
2456 break;
2457 }
2458 case DICTIONARY_ELEMENTS:
2459 CopyDictionaryToObjectElements(isolate, from, from_start, to, to_kind,
2460 to_start, copy_size);
2461 break;
2462 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
2463 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
2464 case FAST_STRING_WRAPPER_ELEMENTS:
2465 case SLOW_STRING_WRAPPER_ELEMENTS:
2466 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
2467 TYPED_ARRAYS(TYPED_ARRAY_CASE)
2468 #undef TYPED_ARRAY_CASE
2469 // This function is currently only used for JSArrays with non-zero
2470 // length.
2471 UNREACHABLE();
2472 case NO_ELEMENTS:
2473 break; // Nothing to do.
2474 }
2475 }
2476
CollectValuesOrEntriesImpl(Isolate * isolate,Handle<JSObject> object,Handle<FixedArray> values_or_entries,bool get_entries,int * nof_items,PropertyFilter filter)2477 static Maybe<bool> CollectValuesOrEntriesImpl(
2478 Isolate* isolate, Handle<JSObject> object,
2479 Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
2480 PropertyFilter filter) {
2481 int count = 0;
2482 if (get_entries) {
2483 // Collecting entries needs to allocate, so this code must be handlified.
2484 Handle<FixedArray> elements(FixedArray::cast(object->elements()),
2485 isolate);
2486 uint32_t length = elements->length();
2487 for (uint32_t index = 0; index < length; ++index) {
2488 InternalIndex entry(index);
2489 if (!Subclass::HasEntryImpl(isolate, *elements, entry)) continue;
2490 Handle<Object> value = Subclass::GetImpl(isolate, *elements, entry);
2491 value = MakeEntryPair(isolate, index, value);
2492 values_or_entries->set(count++, *value);
2493 }
2494 } else {
2495 // No allocations here, so we can avoid handlification overhead.
2496 DisallowHeapAllocation no_gc;
2497 FixedArray elements = FixedArray::cast(object->elements());
2498 uint32_t length = elements.length();
2499 for (uint32_t index = 0; index < length; ++index) {
2500 InternalIndex entry(index);
2501 if (!Subclass::HasEntryImpl(isolate, elements, entry)) continue;
2502 Object value = GetRaw(elements, entry);
2503 values_or_entries->set(count++, value);
2504 }
2505 }
2506 *nof_items = count;
2507 return Just(true);
2508 }
2509
IndexOfValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> search_value,size_t start_from,size_t length)2510 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
2511 Handle<JSObject> receiver,
2512 Handle<Object> search_value,
2513 size_t start_from, size_t length) {
2514 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
2515 DisallowHeapAllocation no_gc;
2516 FixedArrayBase elements_base = receiver->elements();
2517 Object value = *search_value;
2518
2519 if (start_from >= length) return Just<int64_t>(-1);
2520
2521 length = std::min(static_cast<size_t>(elements_base.length()), length);
2522
2523 // Only FAST_{,HOLEY_}ELEMENTS can store non-numbers.
2524 if (!value.IsNumber() && !IsObjectElementsKind(Subclass::kind()) &&
2525 !IsAnyNonextensibleElementsKind(Subclass::kind())) {
2526 return Just<int64_t>(-1);
2527 }
2528 // NaN can never be found by strict equality.
2529 if (value.IsNaN()) return Just<int64_t>(-1);
2530
2531 // k can be greater than receiver->length() below, but it is bounded by
2532 // elements_base->length() so we never read out of bounds. This means that
2533 // elements->get(k) can return the hole, for which the StrictEquals will
2534 // always fail.
2535 FixedArray elements = FixedArray::cast(receiver->elements());
2536 STATIC_ASSERT(FixedArray::kMaxLength <=
2537 std::numeric_limits<uint32_t>::max());
2538 for (size_t k = start_from; k < length; ++k) {
2539 if (value.StrictEquals(elements.get(static_cast<uint32_t>(k)))) {
2540 return Just<int64_t>(k);
2541 }
2542 }
2543 return Just<int64_t>(-1);
2544 }
2545 };
2546
2547 class FastPackedSmiElementsAccessor
2548 : public FastSmiOrObjectElementsAccessor<
2549 FastPackedSmiElementsAccessor,
2550 ElementsKindTraits<PACKED_SMI_ELEMENTS>> {};
2551
2552 class FastHoleySmiElementsAccessor
2553 : public FastSmiOrObjectElementsAccessor<
2554 FastHoleySmiElementsAccessor,
2555 ElementsKindTraits<HOLEY_SMI_ELEMENTS>> {};
2556
2557 class FastPackedObjectElementsAccessor
2558 : public FastSmiOrObjectElementsAccessor<
2559 FastPackedObjectElementsAccessor,
2560 ElementsKindTraits<PACKED_ELEMENTS>> {};
2561
2562 template <typename Subclass, typename KindTraits>
2563 class FastNonextensibleObjectElementsAccessor
2564 : public FastSmiOrObjectElementsAccessor<Subclass, KindTraits> {
2565 public:
2566 using BackingStore = typename KindTraits::BackingStore;
2567
PushImpl(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t push_size)2568 static uint32_t PushImpl(Handle<JSArray> receiver, BuiltinArguments* args,
2569 uint32_t push_size) {
2570 UNREACHABLE();
2571 }
2572
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)2573 static void AddImpl(Handle<JSObject> object, uint32_t index,
2574 Handle<Object> value, PropertyAttributes attributes,
2575 uint32_t new_capacity) {
2576 UNREACHABLE();
2577 }
2578
2579 // TODO(duongn): refactor this due to code duplication of sealed version.
2580 // Consider using JSObject::NormalizeElements(). Also consider follow the fast
2581 // element logic instead of changing to dictionary mode.
SetLengthImpl(Isolate * isolate,Handle<JSArray> array,uint32_t length,Handle<FixedArrayBase> backing_store)2582 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
2583 uint32_t length,
2584 Handle<FixedArrayBase> backing_store) {
2585 uint32_t old_length = 0;
2586 CHECK(array->length().ToArrayIndex(&old_length));
2587 if (length == old_length) {
2588 // Do nothing.
2589 return;
2590 }
2591
2592 // Transition to DICTIONARY_ELEMENTS.
2593 // Convert to dictionary mode.
2594 Handle<NumberDictionary> new_element_dictionary =
2595 old_length == 0 ? isolate->factory()->empty_slow_element_dictionary()
2596 : array->GetElementsAccessor()->Normalize(array);
2597
2598 // Migrate map.
2599 Handle<Map> new_map = Map::Copy(isolate, handle(array->map(), isolate),
2600 "SlowCopyForSetLengthImpl");
2601 new_map->set_is_extensible(false);
2602 new_map->set_elements_kind(DICTIONARY_ELEMENTS);
2603 JSObject::MigrateToMap(isolate, array, new_map);
2604
2605 if (!new_element_dictionary.is_null()) {
2606 array->set_elements(*new_element_dictionary);
2607 }
2608
2609 if (array->elements() !=
2610 ReadOnlyRoots(isolate).empty_slow_element_dictionary()) {
2611 Handle<NumberDictionary> dictionary(array->element_dictionary(), isolate);
2612 // Make sure we never go back to the fast case
2613 array->RequireSlowElements(*dictionary);
2614 JSObject::ApplyAttributesToDictionary(isolate, ReadOnlyRoots(isolate),
2615 dictionary,
2616 PropertyAttributes::NONE);
2617 }
2618
2619 // Set length.
2620 Handle<FixedArrayBase> new_backing_store(array->elements(), isolate);
2621 DictionaryElementsAccessor::SetLengthImpl(isolate, array, length,
2622 new_backing_store);
2623 }
2624 };
2625
2626 class FastPackedNonextensibleObjectElementsAccessor
2627 : public FastNonextensibleObjectElementsAccessor<
2628 FastPackedNonextensibleObjectElementsAccessor,
2629 ElementsKindTraits<PACKED_NONEXTENSIBLE_ELEMENTS>> {};
2630
2631 class FastHoleyNonextensibleObjectElementsAccessor
2632 : public FastNonextensibleObjectElementsAccessor<
2633 FastHoleyNonextensibleObjectElementsAccessor,
2634 ElementsKindTraits<HOLEY_NONEXTENSIBLE_ELEMENTS>> {};
2635
2636 template <typename Subclass, typename KindTraits>
2637 class FastSealedObjectElementsAccessor
2638 : public FastSmiOrObjectElementsAccessor<Subclass, KindTraits> {
2639 public:
2640 using BackingStore = typename KindTraits::BackingStore;
2641
RemoveElement(Handle<JSArray> receiver,Where remove_position)2642 static Handle<Object> RemoveElement(Handle<JSArray> receiver,
2643 Where remove_position) {
2644 UNREACHABLE();
2645 }
2646
DeleteImpl(Handle<JSObject> obj,InternalIndex entry)2647 static void DeleteImpl(Handle<JSObject> obj, InternalIndex entry) {
2648 UNREACHABLE();
2649 }
2650
DeleteAtEnd(Handle<JSObject> obj,Handle<BackingStore> backing_store,uint32_t entry)2651 static void DeleteAtEnd(Handle<JSObject> obj,
2652 Handle<BackingStore> backing_store, uint32_t entry) {
2653 UNREACHABLE();
2654 }
2655
DeleteCommon(Handle<JSObject> obj,uint32_t entry,Handle<FixedArrayBase> store)2656 static void DeleteCommon(Handle<JSObject> obj, uint32_t entry,
2657 Handle<FixedArrayBase> store) {
2658 UNREACHABLE();
2659 }
2660
PopImpl(Handle<JSArray> receiver)2661 static Handle<Object> PopImpl(Handle<JSArray> receiver) { UNREACHABLE(); }
2662
PushImpl(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t push_size)2663 static uint32_t PushImpl(Handle<JSArray> receiver, BuiltinArguments* args,
2664 uint32_t push_size) {
2665 UNREACHABLE();
2666 }
2667
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)2668 static void AddImpl(Handle<JSObject> object, uint32_t index,
2669 Handle<Object> value, PropertyAttributes attributes,
2670 uint32_t new_capacity) {
2671 UNREACHABLE();
2672 }
2673
2674 // TODO(duongn): refactor this due to code duplication of nonextensible
2675 // version. Consider using JSObject::NormalizeElements(). Also consider follow
2676 // the fast element logic instead of changing to dictionary mode.
SetLengthImpl(Isolate * isolate,Handle<JSArray> array,uint32_t length,Handle<FixedArrayBase> backing_store)2677 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
2678 uint32_t length,
2679 Handle<FixedArrayBase> backing_store) {
2680 uint32_t old_length = 0;
2681 CHECK(array->length().ToArrayIndex(&old_length));
2682 if (length == old_length) {
2683 // Do nothing.
2684 return;
2685 }
2686
2687 // Transition to DICTIONARY_ELEMENTS.
2688 // Convert to dictionary mode
2689 Handle<NumberDictionary> new_element_dictionary =
2690 old_length == 0 ? isolate->factory()->empty_slow_element_dictionary()
2691 : array->GetElementsAccessor()->Normalize(array);
2692
2693 // Migrate map.
2694 Handle<Map> new_map = Map::Copy(isolate, handle(array->map(), isolate),
2695 "SlowCopyForSetLengthImpl");
2696 new_map->set_is_extensible(false);
2697 new_map->set_elements_kind(DICTIONARY_ELEMENTS);
2698 JSObject::MigrateToMap(isolate, array, new_map);
2699
2700 if (!new_element_dictionary.is_null()) {
2701 array->set_elements(*new_element_dictionary);
2702 }
2703
2704 if (array->elements() !=
2705 ReadOnlyRoots(isolate).empty_slow_element_dictionary()) {
2706 Handle<NumberDictionary> dictionary(array->element_dictionary(), isolate);
2707 // Make sure we never go back to the fast case
2708 array->RequireSlowElements(*dictionary);
2709 JSObject::ApplyAttributesToDictionary(isolate, ReadOnlyRoots(isolate),
2710 dictionary,
2711 PropertyAttributes::SEALED);
2712 }
2713
2714 // Set length
2715 Handle<FixedArrayBase> new_backing_store(array->elements(), isolate);
2716 DictionaryElementsAccessor::SetLengthImpl(isolate, array, length,
2717 new_backing_store);
2718 }
2719 };
2720
2721 class FastPackedSealedObjectElementsAccessor
2722 : public FastSealedObjectElementsAccessor<
2723 FastPackedSealedObjectElementsAccessor,
2724 ElementsKindTraits<PACKED_SEALED_ELEMENTS>> {};
2725
2726 class FastHoleySealedObjectElementsAccessor
2727 : public FastSealedObjectElementsAccessor<
2728 FastHoleySealedObjectElementsAccessor,
2729 ElementsKindTraits<HOLEY_SEALED_ELEMENTS>> {};
2730
2731 template <typename Subclass, typename KindTraits>
2732 class FastFrozenObjectElementsAccessor
2733 : public FastSmiOrObjectElementsAccessor<Subclass, KindTraits> {
2734 public:
2735 using BackingStore = typename KindTraits::BackingStore;
2736
SetImpl(Handle<JSObject> holder,InternalIndex entry,Object value)2737 static inline void SetImpl(Handle<JSObject> holder, InternalIndex entry,
2738 Object value) {
2739 UNREACHABLE();
2740 }
2741
SetImpl(FixedArrayBase backing_store,InternalIndex entry,Object value)2742 static inline void SetImpl(FixedArrayBase backing_store, InternalIndex entry,
2743 Object value) {
2744 UNREACHABLE();
2745 }
2746
SetImpl(FixedArrayBase backing_store,InternalIndex entry,Object value,WriteBarrierMode mode)2747 static inline void SetImpl(FixedArrayBase backing_store, InternalIndex entry,
2748 Object value, WriteBarrierMode mode) {
2749 UNREACHABLE();
2750 }
2751
RemoveElement(Handle<JSArray> receiver,Where remove_position)2752 static Handle<Object> RemoveElement(Handle<JSArray> receiver,
2753 Where remove_position) {
2754 UNREACHABLE();
2755 }
2756
DeleteImpl(Handle<JSObject> obj,InternalIndex entry)2757 static void DeleteImpl(Handle<JSObject> obj, InternalIndex entry) {
2758 UNREACHABLE();
2759 }
2760
DeleteAtEnd(Handle<JSObject> obj,Handle<BackingStore> backing_store,uint32_t entry)2761 static void DeleteAtEnd(Handle<JSObject> obj,
2762 Handle<BackingStore> backing_store, uint32_t entry) {
2763 UNREACHABLE();
2764 }
2765
DeleteCommon(Handle<JSObject> obj,uint32_t entry,Handle<FixedArrayBase> store)2766 static void DeleteCommon(Handle<JSObject> obj, uint32_t entry,
2767 Handle<FixedArrayBase> store) {
2768 UNREACHABLE();
2769 }
2770
PopImpl(Handle<JSArray> receiver)2771 static Handle<Object> PopImpl(Handle<JSArray> receiver) { UNREACHABLE(); }
2772
PushImpl(Handle<JSArray> receiver,BuiltinArguments * args,uint32_t push_size)2773 static uint32_t PushImpl(Handle<JSArray> receiver, BuiltinArguments* args,
2774 uint32_t push_size) {
2775 UNREACHABLE();
2776 }
2777
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)2778 static void AddImpl(Handle<JSObject> object, uint32_t index,
2779 Handle<Object> value, PropertyAttributes attributes,
2780 uint32_t new_capacity) {
2781 UNREACHABLE();
2782 }
2783
SetLengthImpl(Isolate * isolate,Handle<JSArray> array,uint32_t length,Handle<FixedArrayBase> backing_store)2784 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
2785 uint32_t length,
2786 Handle<FixedArrayBase> backing_store) {
2787 UNREACHABLE();
2788 }
2789
ReconfigureImpl(Handle<JSObject> object,Handle<FixedArrayBase> store,InternalIndex entry,Handle<Object> value,PropertyAttributes attributes)2790 static void ReconfigureImpl(Handle<JSObject> object,
2791 Handle<FixedArrayBase> store, InternalIndex entry,
2792 Handle<Object> value,
2793 PropertyAttributes attributes) {
2794 UNREACHABLE();
2795 }
2796 };
2797
2798 class FastPackedFrozenObjectElementsAccessor
2799 : public FastFrozenObjectElementsAccessor<
2800 FastPackedFrozenObjectElementsAccessor,
2801 ElementsKindTraits<PACKED_FROZEN_ELEMENTS>> {};
2802
2803 class FastHoleyFrozenObjectElementsAccessor
2804 : public FastFrozenObjectElementsAccessor<
2805 FastHoleyFrozenObjectElementsAccessor,
2806 ElementsKindTraits<HOLEY_FROZEN_ELEMENTS>> {};
2807
2808 class FastHoleyObjectElementsAccessor
2809 : public FastSmiOrObjectElementsAccessor<
2810 FastHoleyObjectElementsAccessor, ElementsKindTraits<HOLEY_ELEMENTS>> {
2811 };
2812
2813 template <typename Subclass, typename KindTraits>
2814 class FastDoubleElementsAccessor
2815 : public FastElementsAccessor<Subclass, KindTraits> {
2816 public:
GetImpl(Isolate * isolate,FixedArrayBase backing_store,InternalIndex entry)2817 static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase backing_store,
2818 InternalIndex entry) {
2819 return FixedDoubleArray::get(FixedDoubleArray::cast(backing_store),
2820 entry.as_int(), isolate);
2821 }
2822
SetImpl(Handle<JSObject> holder,InternalIndex entry,Object value)2823 static inline void SetImpl(Handle<JSObject> holder, InternalIndex entry,
2824 Object value) {
2825 SetImpl(holder->elements(), entry, value);
2826 }
2827
SetImpl(FixedArrayBase backing_store,InternalIndex entry,Object value)2828 static inline void SetImpl(FixedArrayBase backing_store, InternalIndex entry,
2829 Object value) {
2830 FixedDoubleArray::cast(backing_store).set(entry.as_int(), value.Number());
2831 }
2832
SetImpl(FixedArrayBase backing_store,InternalIndex entry,Object value,WriteBarrierMode mode)2833 static inline void SetImpl(FixedArrayBase backing_store, InternalIndex entry,
2834 Object value, WriteBarrierMode mode) {
2835 FixedDoubleArray::cast(backing_store).set(entry.as_int(), value.Number());
2836 }
2837
CopyElementsImpl(Isolate * isolate,FixedArrayBase from,uint32_t from_start,FixedArrayBase to,ElementsKind from_kind,uint32_t to_start,int packed_size,int copy_size)2838 static void CopyElementsImpl(Isolate* isolate, FixedArrayBase from,
2839 uint32_t from_start, FixedArrayBase to,
2840 ElementsKind from_kind, uint32_t to_start,
2841 int packed_size, int copy_size) {
2842 DisallowHeapAllocation no_allocation;
2843 switch (from_kind) {
2844 case PACKED_SMI_ELEMENTS:
2845 CopyPackedSmiToDoubleElements(from, from_start, to, to_start,
2846 packed_size, copy_size);
2847 break;
2848 case HOLEY_SMI_ELEMENTS:
2849 CopySmiToDoubleElements(from, from_start, to, to_start, copy_size);
2850 break;
2851 case PACKED_DOUBLE_ELEMENTS:
2852 case HOLEY_DOUBLE_ELEMENTS:
2853 CopyDoubleToDoubleElements(from, from_start, to, to_start, copy_size);
2854 break;
2855 case PACKED_ELEMENTS:
2856 case PACKED_FROZEN_ELEMENTS:
2857 case PACKED_SEALED_ELEMENTS:
2858 case PACKED_NONEXTENSIBLE_ELEMENTS:
2859 case HOLEY_ELEMENTS:
2860 case HOLEY_FROZEN_ELEMENTS:
2861 case HOLEY_SEALED_ELEMENTS:
2862 case HOLEY_NONEXTENSIBLE_ELEMENTS:
2863 CopyObjectToDoubleElements(from, from_start, to, to_start, copy_size);
2864 break;
2865 case DICTIONARY_ELEMENTS:
2866 CopyDictionaryToDoubleElements(isolate, from, from_start, to, to_start,
2867 copy_size);
2868 break;
2869 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
2870 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
2871 case FAST_STRING_WRAPPER_ELEMENTS:
2872 case SLOW_STRING_WRAPPER_ELEMENTS:
2873 case NO_ELEMENTS:
2874 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
2875 TYPED_ARRAYS(TYPED_ARRAY_CASE)
2876 #undef TYPED_ARRAY_CASE
2877 // This function is currently only used for JSArrays with non-zero
2878 // length.
2879 UNREACHABLE();
2880 }
2881 }
2882
CollectValuesOrEntriesImpl(Isolate * isolate,Handle<JSObject> object,Handle<FixedArray> values_or_entries,bool get_entries,int * nof_items,PropertyFilter filter)2883 static Maybe<bool> CollectValuesOrEntriesImpl(
2884 Isolate* isolate, Handle<JSObject> object,
2885 Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
2886 PropertyFilter filter) {
2887 Handle<FixedDoubleArray> elements(
2888 FixedDoubleArray::cast(object->elements()), isolate);
2889 int count = 0;
2890 uint32_t length = elements->length();
2891 for (uint32_t index = 0; index < length; ++index) {
2892 InternalIndex entry(index);
2893 if (!Subclass::HasEntryImpl(isolate, *elements, entry)) continue;
2894 Handle<Object> value = Subclass::GetImpl(isolate, *elements, entry);
2895 if (get_entries) {
2896 value = MakeEntryPair(isolate, index, value);
2897 }
2898 values_or_entries->set(count++, *value);
2899 }
2900 *nof_items = count;
2901 return Just(true);
2902 }
2903
IndexOfValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> search_value,size_t start_from,size_t length)2904 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
2905 Handle<JSObject> receiver,
2906 Handle<Object> search_value,
2907 size_t start_from, size_t length) {
2908 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
2909 DisallowHeapAllocation no_gc;
2910 FixedArrayBase elements_base = receiver->elements();
2911 Object value = *search_value;
2912
2913 length = std::min(static_cast<size_t>(elements_base.length()), length);
2914
2915 if (start_from >= length) return Just<int64_t>(-1);
2916
2917 if (!value.IsNumber()) {
2918 return Just<int64_t>(-1);
2919 }
2920 if (value.IsNaN()) {
2921 return Just<int64_t>(-1);
2922 }
2923 double numeric_search_value = value.Number();
2924 FixedDoubleArray elements = FixedDoubleArray::cast(receiver->elements());
2925
2926 STATIC_ASSERT(FixedDoubleArray::kMaxLength <=
2927 std::numeric_limits<int>::max());
2928 for (size_t k = start_from; k < length; ++k) {
2929 int k_int = static_cast<int>(k);
2930 if (elements.is_the_hole(k_int)) {
2931 continue;
2932 }
2933 if (elements.get_scalar(k_int) == numeric_search_value) {
2934 return Just<int64_t>(k);
2935 }
2936 }
2937 return Just<int64_t>(-1);
2938 }
2939 };
2940
2941 class FastPackedDoubleElementsAccessor
2942 : public FastDoubleElementsAccessor<
2943 FastPackedDoubleElementsAccessor,
2944 ElementsKindTraits<PACKED_DOUBLE_ELEMENTS>> {};
2945
2946 class FastHoleyDoubleElementsAccessor
2947 : public FastDoubleElementsAccessor<
2948 FastHoleyDoubleElementsAccessor,
2949 ElementsKindTraits<HOLEY_DOUBLE_ELEMENTS>> {};
2950
2951 // Super class for all external element arrays.
2952 template <ElementsKind Kind, typename ElementType>
2953 class TypedElementsAccessor
2954 : public ElementsAccessorBase<TypedElementsAccessor<Kind, ElementType>,
2955 ElementsKindTraits<Kind>> {
2956 public:
2957 using BackingStore = typename ElementsKindTraits<Kind>::BackingStore;
2958 using AccessorClass = TypedElementsAccessor<Kind, ElementType>;
2959
2960 // Conversions from (other) scalar values.
FromScalar(int value)2961 static ElementType FromScalar(int value) {
2962 return static_cast<ElementType>(value);
2963 }
FromScalar(uint32_t value)2964 static ElementType FromScalar(uint32_t value) {
2965 return static_cast<ElementType>(value);
2966 }
FromScalar(double value)2967 static ElementType FromScalar(double value) {
2968 return FromScalar(DoubleToInt32(value));
2969 }
FromScalar(int64_t value)2970 static ElementType FromScalar(int64_t value) { UNREACHABLE(); }
FromScalar(uint64_t value)2971 static ElementType FromScalar(uint64_t value) { UNREACHABLE(); }
2972
2973 // Conversions from objects / handles.
FromObject(Object value,bool * lossless=nullptr)2974 static ElementType FromObject(Object value, bool* lossless = nullptr) {
2975 if (value.IsSmi()) {
2976 return FromScalar(Smi::ToInt(value));
2977 } else if (value.IsHeapNumber()) {
2978 return FromScalar(HeapNumber::cast(value).value());
2979 } else {
2980 // Clamp undefined here as well. All other types have been
2981 // converted to a number type further up in the call chain.
2982 DCHECK(value.IsUndefined());
2983 return FromScalar(Oddball::cast(value).to_number_raw());
2984 }
2985 }
FromHandle(Handle<Object> value,bool * lossless=nullptr)2986 static ElementType FromHandle(Handle<Object> value,
2987 bool* lossless = nullptr) {
2988 return FromObject(*value, lossless);
2989 }
2990
2991 // Conversion of scalar value to handlified object.
2992 static Handle<Object> ToHandle(Isolate* isolate, ElementType value);
2993
SetImpl(Handle<JSObject> holder,InternalIndex entry,Object value)2994 static void SetImpl(Handle<JSObject> holder, InternalIndex entry,
2995 Object value) {
2996 Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(holder);
2997 DCHECK_LE(entry.raw_value(), typed_array->length());
2998 SetImpl(static_cast<ElementType*>(typed_array->DataPtr()),
2999 entry.raw_value(), FromObject(value));
3000 }
3001
SetImpl(ElementType * data_ptr,size_t entry,ElementType value)3002 static void SetImpl(ElementType* data_ptr, size_t entry, ElementType value) {
3003 // The JavaScript memory model allows for racy reads and writes to a
3004 // SharedArrayBuffer's backing store. ThreadSanitizer will catch these
3005 // racy accesses and warn about them, so we disable TSAN for these reads
3006 // and writes using annotations.
3007 //
3008 // We don't use relaxed atomics here, as it is not a requirement of the
3009 // JavaScript memory model to have tear-free reads of overlapping accesses,
3010 // and using relaxed atomics may introduce overhead.
3011 TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
3012 if (COMPRESS_POINTERS_BOOL && alignof(ElementType) > kTaggedSize) {
3013 // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
3014 // fields (external pointers, doubles and BigInt data) are only
3015 // kTaggedSize aligned so we have to use unaligned pointer friendly way of
3016 // accessing them in order to avoid undefined behavior in C++ code.
3017 base::WriteUnalignedValue<ElementType>(
3018 reinterpret_cast<Address>(data_ptr + entry), value);
3019 } else {
3020 data_ptr[entry] = value;
3021 }
3022 TSAN_ANNOTATE_IGNORE_WRITES_END;
3023 }
3024
GetInternalImpl(Handle<JSObject> holder,InternalIndex entry)3025 static Handle<Object> GetInternalImpl(Handle<JSObject> holder,
3026 InternalIndex entry) {
3027 Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(holder);
3028 Isolate* isolate = typed_array->GetIsolate();
3029 DCHECK_LE(entry.raw_value(), typed_array->length());
3030 DCHECK(!typed_array->WasDetached());
3031 ElementType elem = GetImpl(
3032 static_cast<ElementType*>(typed_array->DataPtr()), entry.raw_value());
3033 return ToHandle(isolate, elem);
3034 }
3035
GetImpl(Isolate * isolate,FixedArrayBase backing_store,InternalIndex entry)3036 static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase backing_store,
3037 InternalIndex entry) {
3038 UNREACHABLE();
3039 }
3040
GetImpl(ElementType * data_ptr,size_t entry)3041 static ElementType GetImpl(ElementType* data_ptr, size_t entry) {
3042 // The JavaScript memory model allows for racy reads and writes to a
3043 // SharedArrayBuffer's backing store. ThreadSanitizer will catch these
3044 // racy accesses and warn about them, so we disable TSAN for these reads
3045 // and writes using annotations.
3046 //
3047 // We don't use relaxed atomics here, as it is not a requirement of the
3048 // JavaScript memory model to have tear-free reads of overlapping accesses,
3049 // and using relaxed atomics may introduce overhead.
3050 TSAN_ANNOTATE_IGNORE_READS_BEGIN;
3051 ElementType result;
3052 if (COMPRESS_POINTERS_BOOL && alignof(ElementType) > kTaggedSize) {
3053 // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
3054 // fields (external pointers, doubles and BigInt data) are only
3055 // kTaggedSize aligned so we have to use unaligned pointer friendly way of
3056 // accessing them in order to avoid undefined behavior in C++ code.
3057 result = base::ReadUnalignedValue<ElementType>(
3058 reinterpret_cast<Address>(data_ptr + entry));
3059 } else {
3060 result = data_ptr[entry];
3061 }
3062 TSAN_ANNOTATE_IGNORE_READS_END;
3063 return result;
3064 }
3065
GetDetailsImpl(JSObject holder,InternalIndex entry)3066 static PropertyDetails GetDetailsImpl(JSObject holder, InternalIndex entry) {
3067 return PropertyDetails(kData, DONT_DELETE, PropertyCellType::kNoCell);
3068 }
3069
GetDetailsImpl(FixedArrayBase backing_store,InternalIndex entry)3070 static PropertyDetails GetDetailsImpl(FixedArrayBase backing_store,
3071 InternalIndex entry) {
3072 return PropertyDetails(kData, DONT_DELETE, PropertyCellType::kNoCell);
3073 }
3074
HasElementImpl(Isolate * isolate,JSObject holder,size_t index,FixedArrayBase backing_store,PropertyFilter filter)3075 static bool HasElementImpl(Isolate* isolate, JSObject holder, size_t index,
3076 FixedArrayBase backing_store,
3077 PropertyFilter filter) {
3078 return index < AccessorClass::GetCapacityImpl(holder, backing_store);
3079 }
3080
HasAccessorsImpl(JSObject holder,FixedArrayBase backing_store)3081 static bool HasAccessorsImpl(JSObject holder, FixedArrayBase backing_store) {
3082 return false;
3083 }
3084
SetLengthImpl(Isolate * isolate,Handle<JSArray> array,uint32_t length,Handle<FixedArrayBase> backing_store)3085 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
3086 uint32_t length,
3087 Handle<FixedArrayBase> backing_store) {
3088 // External arrays do not support changing their length.
3089 UNREACHABLE();
3090 }
3091
DeleteImpl(Handle<JSObject> obj,InternalIndex entry)3092 static void DeleteImpl(Handle<JSObject> obj, InternalIndex entry) {
3093 UNREACHABLE();
3094 }
3095
GetEntryForIndexImpl(Isolate * isolate,JSObject holder,FixedArrayBase backing_store,size_t index,PropertyFilter filter)3096 static InternalIndex GetEntryForIndexImpl(Isolate* isolate, JSObject holder,
3097 FixedArrayBase backing_store,
3098 size_t index,
3099 PropertyFilter filter) {
3100 return index < AccessorClass::GetCapacityImpl(holder, backing_store)
3101 ? InternalIndex(index)
3102 : InternalIndex::NotFound();
3103 }
3104
GetCapacityImpl(JSObject holder,FixedArrayBase backing_store)3105 static size_t GetCapacityImpl(JSObject holder, FixedArrayBase backing_store) {
3106 JSTypedArray typed_array = JSTypedArray::cast(holder);
3107 if (typed_array.WasDetached()) return 0;
3108 return typed_array.length();
3109 }
3110
NumberOfElementsImpl(JSObject receiver,FixedArrayBase backing_store)3111 static size_t NumberOfElementsImpl(JSObject receiver,
3112 FixedArrayBase backing_store) {
3113 return AccessorClass::GetCapacityImpl(receiver, backing_store);
3114 }
3115
AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,KeyAccumulator * accumulator,AddKeyConversion convert)3116 V8_WARN_UNUSED_RESULT static ExceptionStatus AddElementsToKeyAccumulatorImpl(
3117 Handle<JSObject> receiver, KeyAccumulator* accumulator,
3118 AddKeyConversion convert) {
3119 Isolate* isolate = receiver->GetIsolate();
3120 Handle<FixedArrayBase> elements(receiver->elements(), isolate);
3121 size_t length = AccessorClass::GetCapacityImpl(*receiver, *elements);
3122 for (size_t i = 0; i < length; i++) {
3123 Handle<Object> value =
3124 AccessorClass::GetInternalImpl(receiver, InternalIndex(i));
3125 RETURN_FAILURE_IF_NOT_SUCCESSFUL(accumulator->AddKey(value, convert));
3126 }
3127 return ExceptionStatus::kSuccess;
3128 }
3129
CollectValuesOrEntriesImpl(Isolate * isolate,Handle<JSObject> object,Handle<FixedArray> values_or_entries,bool get_entries,int * nof_items,PropertyFilter filter)3130 static Maybe<bool> CollectValuesOrEntriesImpl(
3131 Isolate* isolate, Handle<JSObject> object,
3132 Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
3133 PropertyFilter filter) {
3134 int count = 0;
3135 if ((filter & ONLY_CONFIGURABLE) == 0) {
3136 Handle<FixedArrayBase> elements(object->elements(), isolate);
3137 size_t length = AccessorClass::GetCapacityImpl(*object, *elements);
3138 for (size_t index = 0; index < length; ++index) {
3139 Handle<Object> value =
3140 AccessorClass::GetInternalImpl(object, InternalIndex(index));
3141 if (get_entries) {
3142 value = MakeEntryPair(isolate, index, value);
3143 }
3144 values_or_entries->set(count++, *value);
3145 }
3146 }
3147 *nof_items = count;
3148 return Just(true);
3149 }
3150
FillImpl(Handle<JSObject> receiver,Handle<Object> value,size_t start,size_t end)3151 static Object FillImpl(Handle<JSObject> receiver, Handle<Object> value,
3152 size_t start, size_t end) {
3153 Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(receiver);
3154 DCHECK(!typed_array->WasDetached());
3155 DCHECK_LE(start, end);
3156 DCHECK_LE(end, typed_array->length());
3157 DisallowHeapAllocation no_gc;
3158 ElementType scalar = FromHandle(value);
3159 ElementType* data = static_cast<ElementType*>(typed_array->DataPtr());
3160 if (COMPRESS_POINTERS_BOOL && alignof(ElementType) > kTaggedSize) {
3161 // TODO(ishell, v8:8875): See UnalignedSlot<T> for details.
3162 std::fill(UnalignedSlot<ElementType>(data + start),
3163 UnalignedSlot<ElementType>(data + end), scalar);
3164 } else {
3165 std::fill(data + start, data + end, scalar);
3166 }
3167 return *typed_array;
3168 }
3169
IncludesValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)3170 static Maybe<bool> IncludesValueImpl(Isolate* isolate,
3171 Handle<JSObject> receiver,
3172 Handle<Object> value, size_t start_from,
3173 size_t length) {
3174 DisallowHeapAllocation no_gc;
3175 JSTypedArray typed_array = JSTypedArray::cast(*receiver);
3176
3177 // TODO(caitp): return Just(false) here when implementing strict throwing on
3178 // detached views.
3179 if (typed_array.WasDetached()) {
3180 return Just(value->IsUndefined(isolate) && length > start_from);
3181 }
3182
3183 if (value->IsUndefined(isolate) && length > typed_array.length()) {
3184 return Just(true);
3185 }
3186
3187 // Prototype has no elements, and not searching for the hole --- limit
3188 // search to backing store length.
3189 if (typed_array.length() < length) {
3190 length = typed_array.length();
3191 }
3192
3193 ElementType typed_search_value;
3194 ElementType* data_ptr =
3195 reinterpret_cast<ElementType*>(typed_array.DataPtr());
3196 if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
3197 if (!value->IsBigInt()) return Just(false);
3198 bool lossless;
3199 typed_search_value = FromHandle(value, &lossless);
3200 if (!lossless) return Just(false);
3201 } else {
3202 if (!value->IsNumber()) return Just(false);
3203 double search_value = value->Number();
3204 if (!std::isfinite(search_value)) {
3205 // Integral types cannot represent +Inf or NaN.
3206 if (Kind < FLOAT32_ELEMENTS || Kind > FLOAT64_ELEMENTS) {
3207 return Just(false);
3208 }
3209 if (std::isnan(search_value)) {
3210 for (size_t k = start_from; k < length; ++k) {
3211 double elem_k =
3212 static_cast<double>(AccessorClass::GetImpl(data_ptr, k));
3213 if (std::isnan(elem_k)) return Just(true);
3214 }
3215 return Just(false);
3216 }
3217 } else if (search_value < std::numeric_limits<ElementType>::lowest() ||
3218 search_value > std::numeric_limits<ElementType>::max()) {
3219 // Return false if value can't be represented in this space.
3220 return Just(false);
3221 }
3222 typed_search_value = static_cast<ElementType>(search_value);
3223 if (static_cast<double>(typed_search_value) != search_value) {
3224 return Just(false); // Loss of precision.
3225 }
3226 }
3227
3228 for (size_t k = start_from; k < length; ++k) {
3229 ElementType elem_k = AccessorClass::GetImpl(data_ptr, k);
3230 if (elem_k == typed_search_value) return Just(true);
3231 }
3232 return Just(false);
3233 }
3234
IndexOfValueImpl(Isolate * isolate,Handle<JSObject> receiver,Handle<Object> value,size_t start_from,size_t length)3235 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
3236 Handle<JSObject> receiver,
3237 Handle<Object> value,
3238 size_t start_from, size_t length) {
3239 DisallowHeapAllocation no_gc;
3240 JSTypedArray typed_array = JSTypedArray::cast(*receiver);
3241
3242 if (typed_array.WasDetached()) return Just<int64_t>(-1);
3243
3244 ElementType typed_search_value;
3245
3246 ElementType* data_ptr =
3247 reinterpret_cast<ElementType*>(typed_array.DataPtr());
3248 if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
3249 if (!value->IsBigInt()) return Just<int64_t>(-1);
3250 bool lossless;
3251 typed_search_value = FromHandle(value, &lossless);
3252 if (!lossless) return Just<int64_t>(-1);
3253 } else {
3254 if (!value->IsNumber()) return Just<int64_t>(-1);
3255 double search_value = value->Number();
3256 if (!std::isfinite(search_value)) {
3257 // Integral types cannot represent +Inf or NaN.
3258 if (Kind < FLOAT32_ELEMENTS || Kind > FLOAT64_ELEMENTS) {
3259 return Just<int64_t>(-1);
3260 }
3261 if (std::isnan(search_value)) {
3262 return Just<int64_t>(-1);
3263 }
3264 } else if (search_value < std::numeric_limits<ElementType>::lowest() ||
3265 search_value > std::numeric_limits<ElementType>::max()) {
3266 // Return false if value can't be represented in this ElementsKind.
3267 return Just<int64_t>(-1);
3268 }
3269 typed_search_value = static_cast<ElementType>(search_value);
3270 if (static_cast<double>(typed_search_value) != search_value) {
3271 return Just<int64_t>(-1); // Loss of precision.
3272 }
3273 }
3274
3275 // Prototype has no elements, and not searching for the hole --- limit
3276 // search to backing store length.
3277 if (typed_array.length() < length) {
3278 length = typed_array.length();
3279 }
3280
3281 for (size_t k = start_from; k < length; ++k) {
3282 ElementType elem_k = AccessorClass::GetImpl(data_ptr, k);
3283 if (elem_k == typed_search_value) return Just<int64_t>(k);
3284 }
3285 return Just<int64_t>(-1);
3286 }
3287
LastIndexOfValueImpl(Handle<JSObject> receiver,Handle<Object> value,size_t start_from)3288 static Maybe<int64_t> LastIndexOfValueImpl(Handle<JSObject> receiver,
3289 Handle<Object> value,
3290 size_t start_from) {
3291 DisallowHeapAllocation no_gc;
3292 JSTypedArray typed_array = JSTypedArray::cast(*receiver);
3293
3294 DCHECK(!typed_array.WasDetached());
3295
3296 ElementType typed_search_value;
3297
3298 ElementType* data_ptr =
3299 reinterpret_cast<ElementType*>(typed_array.DataPtr());
3300 if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
3301 if (!value->IsBigInt()) return Just<int64_t>(-1);
3302 bool lossless;
3303 typed_search_value = FromHandle(value, &lossless);
3304 if (!lossless) return Just<int64_t>(-1);
3305 } else {
3306 if (!value->IsNumber()) return Just<int64_t>(-1);
3307 double search_value = value->Number();
3308 if (!std::isfinite(search_value)) {
3309 if (std::is_integral<ElementType>::value) {
3310 // Integral types cannot represent +Inf or NaN.
3311 return Just<int64_t>(-1);
3312 } else if (std::isnan(search_value)) {
3313 // Strict Equality Comparison of NaN is always false.
3314 return Just<int64_t>(-1);
3315 }
3316 } else if (search_value < std::numeric_limits<ElementType>::lowest() ||
3317 search_value > std::numeric_limits<ElementType>::max()) {
3318 // Return -1 if value can't be represented in this ElementsKind.
3319 return Just<int64_t>(-1);
3320 }
3321 typed_search_value = static_cast<ElementType>(search_value);
3322 if (static_cast<double>(typed_search_value) != search_value) {
3323 return Just<int64_t>(-1); // Loss of precision.
3324 }
3325 }
3326
3327 DCHECK_LT(start_from, typed_array.length());
3328 size_t k = start_from;
3329 do {
3330 ElementType elem_k = AccessorClass::GetImpl(data_ptr, k);
3331 if (elem_k == typed_search_value) return Just<int64_t>(k);
3332 } while (k-- != 0);
3333 return Just<int64_t>(-1);
3334 }
3335
ReverseImpl(JSObject receiver)3336 static void ReverseImpl(JSObject receiver) {
3337 DisallowHeapAllocation no_gc;
3338 JSTypedArray typed_array = JSTypedArray::cast(receiver);
3339
3340 DCHECK(!typed_array.WasDetached());
3341
3342 size_t len = typed_array.length();
3343 if (len == 0) return;
3344
3345 ElementType* data = static_cast<ElementType*>(typed_array.DataPtr());
3346 if (COMPRESS_POINTERS_BOOL && alignof(ElementType) > kTaggedSize) {
3347 // TODO(ishell, v8:8875): See UnalignedSlot<T> for details.
3348 std::reverse(UnalignedSlot<ElementType>(data),
3349 UnalignedSlot<ElementType>(data + len));
3350 } else {
3351 std::reverse(data, data + len);
3352 }
3353 }
3354
CreateListFromArrayLikeImpl(Isolate * isolate,Handle<JSObject> object,uint32_t length)3355 static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
3356 Handle<JSObject> object,
3357 uint32_t length) {
3358 Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object);
3359 Handle<FixedArray> result = isolate->factory()->NewFixedArray(length);
3360 for (uint32_t i = 0; i < length; i++) {
3361 Handle<Object> value =
3362 AccessorClass::GetInternalImpl(typed_array, InternalIndex(i));
3363 result->set(i, *value);
3364 }
3365 return result;
3366 }
3367
CopyTypedArrayElementsSliceImpl(JSTypedArray source,JSTypedArray destination,size_t start,size_t end)3368 static void CopyTypedArrayElementsSliceImpl(JSTypedArray source,
3369 JSTypedArray destination,
3370 size_t start, size_t end) {
3371 DisallowHeapAllocation no_gc;
3372 DCHECK_EQ(destination.GetElementsKind(), AccessorClass::kind());
3373 CHECK(!source.WasDetached());
3374 CHECK(!destination.WasDetached());
3375 DCHECK_LE(start, end);
3376 DCHECK_LE(end, source.length());
3377 size_t count = end - start;
3378 DCHECK_LE(count, destination.length());
3379 ElementType* dest_data = static_cast<ElementType*>(destination.DataPtr());
3380 switch (source.GetElementsKind()) {
3381 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
3382 case TYPE##_ELEMENTS: { \
3383 ctype* source_data = reinterpret_cast<ctype*>(source.DataPtr()) + start; \
3384 CopyBetweenBackingStores<TYPE##_ELEMENTS, ctype>(source_data, dest_data, \
3385 count); \
3386 break; \
3387 }
3388 TYPED_ARRAYS(TYPED_ARRAY_CASE)
3389 #undef TYPED_ARRAY_CASE
3390 default:
3391 UNREACHABLE();
3392 break;
3393 }
3394 }
3395
HasSimpleRepresentation(ExternalArrayType type)3396 static bool HasSimpleRepresentation(ExternalArrayType type) {
3397 return !(type == kExternalFloat32Array || type == kExternalFloat64Array ||
3398 type == kExternalUint8ClampedArray);
3399 }
3400
3401 template <ElementsKind SourceKind, typename SourceElementType>
CopyBetweenBackingStores(SourceElementType * source_data_ptr,ElementType * dest_data_ptr,size_t length)3402 static void CopyBetweenBackingStores(SourceElementType* source_data_ptr,
3403 ElementType* dest_data_ptr,
3404 size_t length) {
3405 DisallowHeapAllocation no_gc;
3406 for (size_t i = 0; i < length; i++) {
3407 // We use scalar accessors to avoid boxing/unboxing, so there are no
3408 // allocations.
3409 SourceElementType source_elem =
3410 TypedElementsAccessor<SourceKind, SourceElementType>::GetImpl(
3411 source_data_ptr, i);
3412 ElementType dest_elem = FromScalar(source_elem);
3413 SetImpl(dest_data_ptr, i, dest_elem);
3414 }
3415 }
3416
CopyElementsFromTypedArray(JSTypedArray source,JSTypedArray destination,size_t length,size_t offset)3417 static void CopyElementsFromTypedArray(JSTypedArray source,
3418 JSTypedArray destination,
3419 size_t length, size_t offset) {
3420 // The source is a typed array, so we know we don't need to do ToNumber
3421 // side-effects, as the source elements will always be a number.
3422 DisallowHeapAllocation no_gc;
3423
3424 CHECK(!source.WasDetached());
3425 CHECK(!destination.WasDetached());
3426
3427 DCHECK_LE(offset, destination.length());
3428 DCHECK_LE(length, destination.length() - offset);
3429 DCHECK_LE(length, source.length());
3430
3431 ExternalArrayType source_type = source.type();
3432 ExternalArrayType destination_type = destination.type();
3433
3434 bool same_type = source_type == destination_type;
3435 bool same_size = source.element_size() == destination.element_size();
3436 bool both_are_simple = HasSimpleRepresentation(source_type) &&
3437 HasSimpleRepresentation(destination_type);
3438
3439 uint8_t* source_data = static_cast<uint8_t*>(source.DataPtr());
3440 uint8_t* dest_data = static_cast<uint8_t*>(destination.DataPtr());
3441 size_t source_byte_length = source.byte_length();
3442 size_t dest_byte_length = destination.byte_length();
3443
3444 // We can simply copy the backing store if the types are the same, or if
3445 // we are converting e.g. Uint8 <-> Int8, as the binary representation
3446 // will be the same. This is not the case for floats or clamped Uint8,
3447 // which have special conversion operations.
3448 if (same_type || (same_size && both_are_simple)) {
3449 size_t element_size = source.element_size();
3450 std::memmove(dest_data + offset * element_size, source_data,
3451 length * element_size);
3452 } else {
3453 std::unique_ptr<uint8_t[]> cloned_source_elements;
3454
3455 // If the typedarrays are overlapped, clone the source.
3456 if (dest_data + dest_byte_length > source_data &&
3457 source_data + source_byte_length > dest_data) {
3458 cloned_source_elements.reset(new uint8_t[source_byte_length]);
3459 std::memcpy(cloned_source_elements.get(), source_data,
3460 source_byte_length);
3461 source_data = cloned_source_elements.get();
3462 }
3463
3464 switch (source.GetElementsKind()) {
3465 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
3466 case TYPE##_ELEMENTS: \
3467 CopyBetweenBackingStores<TYPE##_ELEMENTS, ctype>( \
3468 reinterpret_cast<ctype*>(source_data), \
3469 reinterpret_cast<ElementType*>(dest_data) + offset, length); \
3470 break;
3471 TYPED_ARRAYS(TYPED_ARRAY_CASE)
3472 default:
3473 UNREACHABLE();
3474 break;
3475 }
3476 #undef TYPED_ARRAY_CASE
3477 }
3478 }
3479
HoleyPrototypeLookupRequired(Isolate * isolate,Context context,JSArray source)3480 static bool HoleyPrototypeLookupRequired(Isolate* isolate, Context context,
3481 JSArray source) {
3482 DisallowHeapAllocation no_gc;
3483 DisallowJavascriptExecution no_js(isolate);
3484
3485 #ifdef V8_ENABLE_FORCE_SLOW_PATH
3486 if (isolate->force_slow_path()) return true;
3487 #endif
3488
3489 Object source_proto = source.map().prototype();
3490
3491 // Null prototypes are OK - we don't need to do prototype chain lookups on
3492 // them.
3493 if (source_proto.IsNull(isolate)) return false;
3494 if (source_proto.IsJSProxy()) return true;
3495 if (!context.native_context().is_initial_array_prototype(
3496 JSObject::cast(source_proto))) {
3497 return true;
3498 }
3499
3500 return !Protectors::IsNoElementsIntact(isolate);
3501 }
3502
TryCopyElementsFastNumber(Context context,JSArray source,JSTypedArray destination,size_t length,size_t offset)3503 static bool TryCopyElementsFastNumber(Context context, JSArray source,
3504 JSTypedArray destination, size_t length,
3505 size_t offset) {
3506 if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) return false;
3507 Isolate* isolate = source.GetIsolate();
3508 DisallowHeapAllocation no_gc;
3509 DisallowJavascriptExecution no_js(isolate);
3510
3511 CHECK(!destination.WasDetached());
3512
3513 size_t current_length;
3514 DCHECK(source.length().IsNumber() &&
3515 TryNumberToSize(source.length(), ¤t_length) &&
3516 length <= current_length);
3517 USE(current_length);
3518
3519 size_t dest_length = destination.length();
3520 DCHECK(length + offset <= dest_length);
3521 USE(dest_length);
3522
3523 ElementsKind kind = source.GetElementsKind();
3524
3525 // When we find the hole, we normally have to look up the element on the
3526 // prototype chain, which is not handled here and we return false instead.
3527 // When the array has the original array prototype, and that prototype has
3528 // not been changed in a way that would affect lookups, we can just convert
3529 // the hole into undefined.
3530 if (HoleyPrototypeLookupRequired(isolate, context, source)) return false;
3531
3532 Oddball undefined = ReadOnlyRoots(isolate).undefined_value();
3533 ElementType* dest_data =
3534 reinterpret_cast<ElementType*>(destination.DataPtr()) + offset;
3535
3536 // Fast-path for packed Smi kind.
3537 if (kind == PACKED_SMI_ELEMENTS) {
3538 FixedArray source_store = FixedArray::cast(source.elements());
3539
3540 for (size_t i = 0; i < length; i++) {
3541 Object elem = source_store.get(static_cast<int>(i));
3542 SetImpl(dest_data, i, FromScalar(Smi::ToInt(elem)));
3543 }
3544 return true;
3545 } else if (kind == HOLEY_SMI_ELEMENTS) {
3546 FixedArray source_store = FixedArray::cast(source.elements());
3547 for (size_t i = 0; i < length; i++) {
3548 if (source_store.is_the_hole(isolate, static_cast<int>(i))) {
3549 SetImpl(dest_data, i, FromObject(undefined));
3550 } else {
3551 Object elem = source_store.get(static_cast<int>(i));
3552 SetImpl(dest_data, i, FromScalar(Smi::ToInt(elem)));
3553 }
3554 }
3555 return true;
3556 } else if (kind == PACKED_DOUBLE_ELEMENTS) {
3557 // Fast-path for packed double kind. We avoid boxing and then immediately
3558 // unboxing the double here by using get_scalar.
3559 FixedDoubleArray source_store = FixedDoubleArray::cast(source.elements());
3560
3561 for (size_t i = 0; i < length; i++) {
3562 // Use the from_double conversion for this specific TypedArray type,
3563 // rather than relying on C++ to convert elem.
3564 double elem = source_store.get_scalar(static_cast<int>(i));
3565 SetImpl(dest_data, i, FromScalar(elem));
3566 }
3567 return true;
3568 } else if (kind == HOLEY_DOUBLE_ELEMENTS) {
3569 FixedDoubleArray source_store = FixedDoubleArray::cast(source.elements());
3570 for (size_t i = 0; i < length; i++) {
3571 if (source_store.is_the_hole(static_cast<int>(i))) {
3572 SetImpl(dest_data, i, FromObject(undefined));
3573 } else {
3574 double elem = source_store.get_scalar(static_cast<int>(i));
3575 SetImpl(dest_data, i, FromScalar(elem));
3576 }
3577 }
3578 return true;
3579 }
3580 return false;
3581 }
3582
CopyElementsHandleSlow(Handle<Object> source,Handle<JSTypedArray> destination,size_t length,size_t offset)3583 static Object CopyElementsHandleSlow(Handle<Object> source,
3584 Handle<JSTypedArray> destination,
3585 size_t length, size_t offset) {
3586 Isolate* isolate = destination->GetIsolate();
3587 for (size_t i = 0; i < length; i++) {
3588 Handle<Object> elem;
3589 LookupIterator it(isolate, source, i);
3590 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
3591 Object::GetProperty(&it));
3592 if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
3593 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
3594 BigInt::FromObject(isolate, elem));
3595 } else {
3596 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
3597 Object::ToNumber(isolate, elem));
3598 }
3599
3600 if (V8_UNLIKELY(destination->WasDetached())) {
3601 const char* op = "set";
3602 const MessageTemplate message = MessageTemplate::kDetachedOperation;
3603 Handle<String> operation =
3604 isolate->factory()->NewStringFromAsciiChecked(op);
3605 THROW_NEW_ERROR_RETURN_FAILURE(isolate,
3606 NewTypeError(message, operation));
3607 }
3608 // The spec says we store the length, then get each element, so we don't
3609 // need to check changes to length.
3610 SetImpl(destination, InternalIndex(offset + i), *elem);
3611 }
3612 return *isolate->factory()->undefined_value();
3613 }
3614
3615 // This doesn't guarantee that the destination array will be completely
3616 // filled. The caller must do this by passing a source with equal length, if
3617 // that is required.
CopyElementsHandleImpl(Handle<Object> source,Handle<JSObject> destination,size_t length,size_t offset)3618 static Object CopyElementsHandleImpl(Handle<Object> source,
3619 Handle<JSObject> destination,
3620 size_t length, size_t offset) {
3621 Isolate* isolate = destination->GetIsolate();
3622 Handle<JSTypedArray> destination_ta =
3623 Handle<JSTypedArray>::cast(destination);
3624 DCHECK_LE(offset + length, destination_ta->length());
3625 CHECK(!destination_ta->WasDetached());
3626
3627 if (length == 0) return *isolate->factory()->undefined_value();
3628
3629 // All conversions from TypedArrays can be done without allocation.
3630 if (source->IsJSTypedArray()) {
3631 Handle<JSTypedArray> source_ta = Handle<JSTypedArray>::cast(source);
3632 ElementsKind source_kind = source_ta->GetElementsKind();
3633 bool source_is_bigint =
3634 source_kind == BIGINT64_ELEMENTS || source_kind == BIGUINT64_ELEMENTS;
3635 bool target_is_bigint =
3636 Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS;
3637 // If we have to copy more elements than we have in the source, we need to
3638 // do special handling and conversion; that happens in the slow case.
3639 if (source_is_bigint == target_is_bigint && !source_ta->WasDetached() &&
3640 length + offset <= source_ta->length()) {
3641 CopyElementsFromTypedArray(*source_ta, *destination_ta, length, offset);
3642 return *isolate->factory()->undefined_value();
3643 }
3644 } else if (source->IsJSArray()) {
3645 // Fast cases for packed numbers kinds where we don't need to allocate.
3646 Handle<JSArray> source_js_array = Handle<JSArray>::cast(source);
3647 size_t current_length;
3648 DCHECK(source_js_array->length().IsNumber());
3649 if (TryNumberToSize(source_js_array->length(), ¤t_length) &&
3650 length <= current_length) {
3651 Handle<JSArray> source_array = Handle<JSArray>::cast(source);
3652 if (TryCopyElementsFastNumber(isolate->context(), *source_array,
3653 *destination_ta, length, offset)) {
3654 return *isolate->factory()->undefined_value();
3655 }
3656 }
3657 }
3658 // Final generic case that handles prototype chain lookups, getters, proxies
3659 // and observable side effects via valueOf, etc.
3660 return CopyElementsHandleSlow(source, destination_ta, length, offset);
3661 }
3662 };
3663
3664 // static
3665 template <>
ToHandle(Isolate * isolate,int8_t value)3666 Handle<Object> TypedElementsAccessor<INT8_ELEMENTS, int8_t>::ToHandle(
3667 Isolate* isolate, int8_t value) {
3668 return handle(Smi::FromInt(value), isolate);
3669 }
3670
3671 // static
3672 template <>
ToHandle(Isolate * isolate,uint8_t value)3673 Handle<Object> TypedElementsAccessor<UINT8_ELEMENTS, uint8_t>::ToHandle(
3674 Isolate* isolate, uint8_t value) {
3675 return handle(Smi::FromInt(value), isolate);
3676 }
3677
3678 // static
3679 template <>
ToHandle(Isolate * isolate,int16_t value)3680 Handle<Object> TypedElementsAccessor<INT16_ELEMENTS, int16_t>::ToHandle(
3681 Isolate* isolate, int16_t value) {
3682 return handle(Smi::FromInt(value), isolate);
3683 }
3684
3685 // static
3686 template <>
ToHandle(Isolate * isolate,uint16_t value)3687 Handle<Object> TypedElementsAccessor<UINT16_ELEMENTS, uint16_t>::ToHandle(
3688 Isolate* isolate, uint16_t value) {
3689 return handle(Smi::FromInt(value), isolate);
3690 }
3691
3692 // static
3693 template <>
ToHandle(Isolate * isolate,int32_t value)3694 Handle<Object> TypedElementsAccessor<INT32_ELEMENTS, int32_t>::ToHandle(
3695 Isolate* isolate, int32_t value) {
3696 return isolate->factory()->NewNumberFromInt(value);
3697 }
3698
3699 // static
3700 template <>
ToHandle(Isolate * isolate,uint32_t value)3701 Handle<Object> TypedElementsAccessor<UINT32_ELEMENTS, uint32_t>::ToHandle(
3702 Isolate* isolate, uint32_t value) {
3703 return isolate->factory()->NewNumberFromUint(value);
3704 }
3705
3706 // static
3707 template <>
FromScalar(double value)3708 float TypedElementsAccessor<FLOAT32_ELEMENTS, float>::FromScalar(double value) {
3709 return DoubleToFloat32(value);
3710 }
3711
3712 // static
3713 template <>
ToHandle(Isolate * isolate,float value)3714 Handle<Object> TypedElementsAccessor<FLOAT32_ELEMENTS, float>::ToHandle(
3715 Isolate* isolate, float value) {
3716 return isolate->factory()->NewNumber(value);
3717 }
3718
3719 // static
3720 template <>
FromScalar(double value)3721 double TypedElementsAccessor<FLOAT64_ELEMENTS, double>::FromScalar(
3722 double value) {
3723 return value;
3724 }
3725
3726 // static
3727 template <>
ToHandle(Isolate * isolate,double value)3728 Handle<Object> TypedElementsAccessor<FLOAT64_ELEMENTS, double>::ToHandle(
3729 Isolate* isolate, double value) {
3730 return isolate->factory()->NewNumber(value);
3731 }
3732
3733 // static
3734 template <>
FromScalar(int value)3735 uint8_t TypedElementsAccessor<UINT8_CLAMPED_ELEMENTS, uint8_t>::FromScalar(
3736 int value) {
3737 if (value < 0x00) return 0x00;
3738 if (value > 0xFF) return 0xFF;
3739 return static_cast<uint8_t>(value);
3740 }
3741
3742 // static
3743 template <>
FromScalar(uint32_t value)3744 uint8_t TypedElementsAccessor<UINT8_CLAMPED_ELEMENTS, uint8_t>::FromScalar(
3745 uint32_t value) {
3746 // We need this special case for Uint32 -> Uint8Clamped, because the highest
3747 // Uint32 values will be negative as an int, clamping to 0, rather than 255.
3748 if (value > 0xFF) return 0xFF;
3749 return static_cast<uint8_t>(value);
3750 }
3751
3752 // static
3753 template <>
FromScalar(double value)3754 uint8_t TypedElementsAccessor<UINT8_CLAMPED_ELEMENTS, uint8_t>::FromScalar(
3755 double value) {
3756 // Handle NaNs and less than zero values which clamp to zero.
3757 if (!(value > 0)) return 0;
3758 if (value > 0xFF) return 0xFF;
3759 return static_cast<uint8_t>(lrint(value));
3760 }
3761
3762 // static
3763 template <>
ToHandle(Isolate * isolate,uint8_t value)3764 Handle<Object> TypedElementsAccessor<UINT8_CLAMPED_ELEMENTS, uint8_t>::ToHandle(
3765 Isolate* isolate, uint8_t value) {
3766 return handle(Smi::FromInt(value), isolate);
3767 }
3768
3769 // static
3770 template <>
FromScalar(int value)3771 int64_t TypedElementsAccessor<BIGINT64_ELEMENTS, int64_t>::FromScalar(
3772 int value) {
3773 UNREACHABLE();
3774 }
3775
3776 // static
3777 template <>
FromScalar(uint32_t value)3778 int64_t TypedElementsAccessor<BIGINT64_ELEMENTS, int64_t>::FromScalar(
3779 uint32_t value) {
3780 UNREACHABLE();
3781 }
3782
3783 // static
3784 template <>
FromScalar(double value)3785 int64_t TypedElementsAccessor<BIGINT64_ELEMENTS, int64_t>::FromScalar(
3786 double value) {
3787 UNREACHABLE();
3788 }
3789
3790 // static
3791 template <>
FromScalar(int64_t value)3792 int64_t TypedElementsAccessor<BIGINT64_ELEMENTS, int64_t>::FromScalar(
3793 int64_t value) {
3794 return value;
3795 }
3796
3797 // static
3798 template <>
FromScalar(uint64_t value)3799 int64_t TypedElementsAccessor<BIGINT64_ELEMENTS, int64_t>::FromScalar(
3800 uint64_t value) {
3801 return static_cast<int64_t>(value);
3802 }
3803
3804 // static
3805 template <>
FromObject(Object value,bool * lossless)3806 int64_t TypedElementsAccessor<BIGINT64_ELEMENTS, int64_t>::FromObject(
3807 Object value, bool* lossless) {
3808 return BigInt::cast(value).AsInt64(lossless);
3809 }
3810
3811 // static
3812 template <>
ToHandle(Isolate * isolate,int64_t value)3813 Handle<Object> TypedElementsAccessor<BIGINT64_ELEMENTS, int64_t>::ToHandle(
3814 Isolate* isolate, int64_t value) {
3815 return BigInt::FromInt64(isolate, value);
3816 }
3817
3818 // static
3819 template <>
FromScalar(int value)3820 uint64_t TypedElementsAccessor<BIGUINT64_ELEMENTS, uint64_t>::FromScalar(
3821 int value) {
3822 UNREACHABLE();
3823 }
3824
3825 // static
3826 template <>
FromScalar(uint32_t value)3827 uint64_t TypedElementsAccessor<BIGUINT64_ELEMENTS, uint64_t>::FromScalar(
3828 uint32_t value) {
3829 UNREACHABLE();
3830 }
3831
3832 // static
3833 template <>
FromScalar(double value)3834 uint64_t TypedElementsAccessor<BIGUINT64_ELEMENTS, uint64_t>::FromScalar(
3835 double value) {
3836 UNREACHABLE();
3837 }
3838
3839 // static
3840 template <>
FromScalar(int64_t value)3841 uint64_t TypedElementsAccessor<BIGUINT64_ELEMENTS, uint64_t>::FromScalar(
3842 int64_t value) {
3843 return static_cast<uint64_t>(value);
3844 }
3845
3846 // static
3847 template <>
FromScalar(uint64_t value)3848 uint64_t TypedElementsAccessor<BIGUINT64_ELEMENTS, uint64_t>::FromScalar(
3849 uint64_t value) {
3850 return value;
3851 }
3852
3853 // static
3854 template <>
FromObject(Object value,bool * lossless)3855 uint64_t TypedElementsAccessor<BIGUINT64_ELEMENTS, uint64_t>::FromObject(
3856 Object value, bool* lossless) {
3857 return BigInt::cast(value).AsUint64(lossless);
3858 }
3859
3860 // static
3861 template <>
ToHandle(Isolate * isolate,uint64_t value)3862 Handle<Object> TypedElementsAccessor<BIGUINT64_ELEMENTS, uint64_t>::ToHandle(
3863 Isolate* isolate, uint64_t value) {
3864 return BigInt::FromUint64(isolate, value);
3865 }
3866
3867 #define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype) \
3868 using Type##ElementsAccessor = TypedElementsAccessor<TYPE##_ELEMENTS, ctype>;
3869 TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR)
3870 #undef FIXED_ELEMENTS_ACCESSOR
3871
3872 template <typename Subclass, typename ArgumentsAccessor, typename KindTraits>
3873 class SloppyArgumentsElementsAccessor
3874 : public ElementsAccessorBase<Subclass, KindTraits> {
3875 public:
ConvertArgumentsStoreResult(Handle<SloppyArgumentsElements> elements,Handle<Object> result)3876 static void ConvertArgumentsStoreResult(
3877 Handle<SloppyArgumentsElements> elements, Handle<Object> result) {
3878 UNREACHABLE();
3879 }
3880
GetImpl(Isolate * isolate,FixedArrayBase parameters,InternalIndex entry)3881 static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase parameters,
3882 InternalIndex entry) {
3883 Handle<SloppyArgumentsElements> elements(
3884 SloppyArgumentsElements::cast(parameters), isolate);
3885 uint32_t length = elements->parameter_map_length();
3886 if (entry.as_uint32() < length) {
3887 // Read context mapped entry.
3888 DisallowHeapAllocation no_gc;
3889 Object probe = elements->get_mapped_entry(entry.as_uint32());
3890 DCHECK(!probe.IsTheHole(isolate));
3891 Context context = elements->context();
3892 int context_entry = Smi::ToInt(probe);
3893 DCHECK(!context.get(context_entry).IsTheHole(isolate));
3894 return handle(context.get(context_entry), isolate);
3895 } else {
3896 // Entry is not context mapped, defer to the arguments.
3897 Handle<Object> result = ArgumentsAccessor::GetImpl(
3898 isolate, elements->arguments(), entry.adjust_down(length));
3899 return Subclass::ConvertArgumentsStoreResult(isolate, elements, result);
3900 }
3901 }
3902
TransitionElementsKindImpl(Handle<JSObject> object,Handle<Map> map)3903 static void TransitionElementsKindImpl(Handle<JSObject> object,
3904 Handle<Map> map) {
3905 UNREACHABLE();
3906 }
3907
GrowCapacityAndConvertImpl(Handle<JSObject> object,uint32_t capacity)3908 static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
3909 uint32_t capacity) {
3910 UNREACHABLE();
3911 }
3912
SetImpl(Handle<JSObject> holder,InternalIndex entry,Object value)3913 static inline void SetImpl(Handle<JSObject> holder, InternalIndex entry,
3914 Object value) {
3915 SetImpl(holder->elements(), entry, value);
3916 }
3917
SetImpl(FixedArrayBase store,InternalIndex entry,Object value)3918 static inline void SetImpl(FixedArrayBase store, InternalIndex entry,
3919 Object value) {
3920 SloppyArgumentsElements elements = SloppyArgumentsElements::cast(store);
3921 uint32_t length = elements.parameter_map_length();
3922 if (entry.as_uint32() < length) {
3923 // Store context mapped entry.
3924 DisallowHeapAllocation no_gc;
3925 Object probe = elements.get_mapped_entry(entry.as_uint32());
3926 DCHECK(!probe.IsTheHole());
3927 Context context = elements.context();
3928 int context_entry = Smi::ToInt(probe);
3929 DCHECK(!context.get(context_entry).IsTheHole());
3930 context.set(context_entry, value);
3931 } else {
3932 // Entry is not context mapped defer to arguments.
3933 FixedArray arguments = elements.arguments();
3934 Object current =
3935 ArgumentsAccessor::GetRaw(arguments, entry.adjust_down(length));
3936 if (current.IsAliasedArgumentsEntry()) {
3937 AliasedArgumentsEntry alias = AliasedArgumentsEntry::cast(current);
3938 Context context = elements.context();
3939 int context_entry = alias.aliased_context_slot();
3940 DCHECK(!context.get(context_entry).IsTheHole());
3941 context.set(context_entry, value);
3942 } else {
3943 ArgumentsAccessor::SetImpl(arguments, entry.adjust_down(length), value);
3944 }
3945 }
3946 }
3947
SetLengthImpl(Isolate * isolate,Handle<JSArray> array,uint32_t length,Handle<FixedArrayBase> parameter_map)3948 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array,
3949 uint32_t length,
3950 Handle<FixedArrayBase> parameter_map) {
3951 // Sloppy arguments objects are not arrays.
3952 UNREACHABLE();
3953 }
3954
GetCapacityImpl(JSObject holder,FixedArrayBase store)3955 static uint32_t GetCapacityImpl(JSObject holder, FixedArrayBase store) {
3956 SloppyArgumentsElements elements = SloppyArgumentsElements::cast(store);
3957 FixedArray arguments = elements.arguments();
3958 return elements.parameter_map_length() +
3959 ArgumentsAccessor::GetCapacityImpl(holder, arguments);
3960 }
3961
GetMaxNumberOfEntries(JSObject holder,FixedArrayBase backing_store)3962 static uint32_t GetMaxNumberOfEntries(JSObject holder,
3963 FixedArrayBase backing_store) {
3964 SloppyArgumentsElements elements =
3965 SloppyArgumentsElements::cast(backing_store);
3966 FixedArrayBase arguments = elements.arguments();
3967 size_t max_entries =
3968 ArgumentsAccessor::GetMaxNumberOfEntries(holder, arguments);
3969 DCHECK_LE(max_entries, std::numeric_limits<uint32_t>::max());
3970 return elements.parameter_map_length() + static_cast<uint32_t>(max_entries);
3971 }
3972
NumberOfElementsImpl(JSObject receiver,FixedArrayBase backing_store)3973 static uint32_t NumberOfElementsImpl(JSObject receiver,
3974 FixedArrayBase backing_store) {
3975 Isolate* isolate = receiver.GetIsolate();
3976 SloppyArgumentsElements elements =
3977 SloppyArgumentsElements::cast(backing_store);
3978 FixedArrayBase arguments = elements.arguments();
3979 uint32_t nof_elements = 0;
3980 uint32_t length = elements.parameter_map_length();
3981 for (uint32_t index = 0; index < length; index++) {
3982 if (HasParameterMapArg(isolate, elements, index)) nof_elements++;
3983 }
3984 return nof_elements +
3985 ArgumentsAccessor::NumberOfElementsImpl(receiver, arguments);
3986 }
3987
AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,KeyAccumulator * accumulator,AddKeyConversion convert)3988 V8_WARN_UNUSED_RESULT static ExceptionStatus AddElementsToKeyAccumulatorImpl(
3989 Handle<JSObject> receiver, KeyAccumulator* accumulator,
3990 AddKeyConversion convert) {
3991 Isolate* isolate = accumulator->isolate();
3992 Handle<FixedArrayBase> elements(receiver->elements(), isolate);
3993 uint32_t length = GetCapacityImpl(*receiver, *elements);
3994 for (uint32_t index = 0; index < length; index++) {
3995 InternalIndex entry(index);
3996 if (!HasEntryImpl(isolate, *elements, entry)) continue;
3997 Handle<Object> value = GetImpl(isolate, *elements, entry);
3998 RETURN_FAILURE_IF_NOT_SUCCESSFUL(accumulator->AddKey(value, convert));
3999 }
4000 return ExceptionStatus::kSuccess;
4001 }
4002
HasEntryImpl(Isolate * isolate,FixedArrayBase parameters,InternalIndex entry)4003 static bool HasEntryImpl(Isolate* isolate, FixedArrayBase parameters,
4004 InternalIndex entry) {
4005 SloppyArgumentsElements elements =
4006 SloppyArgumentsElements::cast(parameters);
4007 uint32_t length = elements.parameter_map_length();
4008 if (entry.raw_value() < length) {
4009 return HasParameterMapArg(isolate, elements, entry.raw_value());
4010 }
4011 FixedArrayBase arguments = elements.arguments();
4012 return ArgumentsAccessor::HasEntryImpl(isolate, arguments,
4013 entry.adjust_down(length));
4014 }
4015
HasAccessorsImpl(JSObject holder,FixedArrayBase backing_store)4016 static bool HasAccessorsImpl(JSObject holder, FixedArrayBase backing_store) {
4017 SloppyArgumentsElements elements =
4018 SloppyArgumentsElements::cast(backing_store);
4019 FixedArray arguments = elements.arguments();
4020 return ArgumentsAccessor::HasAccessorsImpl(holder, arguments);
4021 }
4022
GetEntryForIndexImpl(Isolate * isolate,JSObject holder,FixedArrayBase parameters,size_t index,PropertyFilter filter)4023 static InternalIndex GetEntryForIndexImpl(Isolate* isolate, JSObject holder,
4024 FixedArrayBase parameters,
4025 size_t index,
4026 PropertyFilter filter) {
4027 SloppyArgumentsElements elements =
4028 SloppyArgumentsElements::cast(parameters);
4029 if (HasParameterMapArg(isolate, elements, index)) {
4030 return InternalIndex(index);
4031 }
4032 FixedArray arguments = elements.arguments();
4033 InternalIndex entry = ArgumentsAccessor::GetEntryForIndexImpl(
4034 isolate, holder, arguments, index, filter);
4035 if (entry.is_not_found()) return entry;
4036 // Arguments entries could overlap with the dictionary entries, hence offset
4037 // them by the number of context mapped entries.
4038 return entry.adjust_up(elements.parameter_map_length());
4039 }
4040
GetDetailsImpl(JSObject holder,InternalIndex entry)4041 static PropertyDetails GetDetailsImpl(JSObject holder, InternalIndex entry) {
4042 SloppyArgumentsElements elements =
4043 SloppyArgumentsElements::cast(holder.elements());
4044 uint32_t length = elements.parameter_map_length();
4045 if (entry.as_uint32() < length) {
4046 return PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
4047 }
4048 FixedArray arguments = elements.arguments();
4049 return ArgumentsAccessor::GetDetailsImpl(arguments,
4050 entry.adjust_down(length));
4051 }
4052
HasParameterMapArg(Isolate * isolate,SloppyArgumentsElements elements,size_t index)4053 static bool HasParameterMapArg(Isolate* isolate,
4054 SloppyArgumentsElements elements,
4055 size_t index) {
4056 uint32_t length = elements.parameter_map_length();
4057 if (index >= length) return false;
4058 return !elements.get_mapped_entry(static_cast<uint32_t>(index))
4059 .IsTheHole(isolate);
4060 }
4061
DeleteImpl(Handle<JSObject> obj,InternalIndex entry)4062 static void DeleteImpl(Handle<JSObject> obj, InternalIndex entry) {
4063 Handle<SloppyArgumentsElements> elements(
4064 SloppyArgumentsElements::cast(obj->elements()), obj->GetIsolate());
4065 uint32_t length = elements->parameter_map_length();
4066 InternalIndex delete_or_entry = entry;
4067 if (entry.as_uint32() < length) {
4068 delete_or_entry = InternalIndex::NotFound();
4069 }
4070 Subclass::SloppyDeleteImpl(obj, elements, delete_or_entry);
4071 // SloppyDeleteImpl allocates a new dictionary elements store. For making
4072 // heap verification happy we postpone clearing out the mapped entry.
4073 if (entry.as_uint32() < length) {
4074 elements->set_mapped_entry(entry.as_uint32(),
4075 obj->GetReadOnlyRoots().the_hole_value());
4076 }
4077 }
4078
SloppyDeleteImpl(Handle<JSObject> obj,Handle<SloppyArgumentsElements> elements,InternalIndex entry)4079 static void SloppyDeleteImpl(Handle<JSObject> obj,
4080 Handle<SloppyArgumentsElements> elements,
4081 InternalIndex entry) {
4082 // Implemented in subclasses.
4083 UNREACHABLE();
4084 }
4085
CollectElementIndicesImpl(Handle<JSObject> object,Handle<FixedArrayBase> backing_store,KeyAccumulator * keys)4086 V8_WARN_UNUSED_RESULT static ExceptionStatus CollectElementIndicesImpl(
4087 Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
4088 KeyAccumulator* keys) {
4089 Isolate* isolate = keys->isolate();
4090 uint32_t nof_indices = 0;
4091 Handle<FixedArray> indices = isolate->factory()->NewFixedArray(
4092 GetCapacityImpl(*object, *backing_store));
4093 DirectCollectElementIndicesImpl(isolate, object, backing_store,
4094 GetKeysConversion::kKeepNumbers,
4095 ENUMERABLE_STRINGS, indices, &nof_indices);
4096 SortIndices(isolate, indices, nof_indices);
4097 for (uint32_t i = 0; i < nof_indices; i++) {
4098 RETURN_FAILURE_IF_NOT_SUCCESSFUL(keys->AddKey(indices->get(i)));
4099 }
4100 return ExceptionStatus::kSuccess;
4101 }
4102
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)4103 static Handle<FixedArray> DirectCollectElementIndicesImpl(
4104 Isolate* isolate, Handle<JSObject> object,
4105 Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
4106 PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices,
4107 uint32_t insertion_index = 0) {
4108 Handle<SloppyArgumentsElements> elements =
4109 Handle<SloppyArgumentsElements>::cast(backing_store);
4110 uint32_t length = elements->parameter_map_length();
4111
4112 for (uint32_t i = 0; i < length; ++i) {
4113 if (elements->get_mapped_entry(i).IsTheHole(isolate)) continue;
4114 if (convert == GetKeysConversion::kConvertToString) {
4115 Handle<String> index_string = isolate->factory()->Uint32ToString(i);
4116 list->set(insertion_index, *index_string);
4117 } else {
4118 list->set(insertion_index, Smi::FromInt(i));
4119 }
4120 insertion_index++;
4121 }
4122
4123 Handle<FixedArray> store(elements->arguments(), isolate);
4124 return ArgumentsAccessor::DirectCollectElementIndicesImpl(
4125 isolate, object, store, convert, filter, list, nof_indices,
4126 insertion_index);
4127 }
4128
IncludesValueImpl(Isolate * isolate,Handle<JSObject> object,Handle<Object> value,size_t start_from,size_t length)4129 static Maybe<bool> IncludesValueImpl(Isolate* isolate,
4130 Handle<JSObject> object,
4131 Handle<Object> value, size_t start_from,
4132 size_t length) {
4133 DCHECK(JSObject::PrototypeHasNoElements(isolate, *object));
4134 Handle<Map> original_map(object->map(), isolate);
4135 Handle<SloppyArgumentsElements> elements(
4136 SloppyArgumentsElements::cast(object->elements()), isolate);
4137 bool search_for_hole = value->IsUndefined(isolate);
4138
4139 for (size_t k = start_from; k < length; ++k) {
4140 DCHECK_EQ(object->map(), *original_map);
4141 InternalIndex entry =
4142 GetEntryForIndexImpl(isolate, *object, *elements, k, ALL_PROPERTIES);
4143 if (entry.is_not_found()) {
4144 if (search_for_hole) return Just(true);
4145 continue;
4146 }
4147
4148 Handle<Object> element_k = Subclass::GetImpl(isolate, *elements, entry);
4149
4150 if (element_k->IsAccessorPair()) {
4151 LookupIterator it(isolate, object, k, LookupIterator::OWN);
4152 DCHECK(it.IsFound());
4153 DCHECK_EQ(it.state(), LookupIterator::ACCESSOR);
4154 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k,
4155 Object::GetPropertyWithAccessor(&it),
4156 Nothing<bool>());
4157
4158 if (value->SameValueZero(*element_k)) return Just(true);
4159
4160 if (object->map() != *original_map) {
4161 // Some mutation occurred in accessor. Abort "fast" path
4162 return IncludesValueSlowPath(isolate, object, value, k + 1, length);
4163 }
4164 } else if (value->SameValueZero(*element_k)) {
4165 return Just(true);
4166 }
4167 }
4168 return Just(false);
4169 }
4170
IndexOfValueImpl(Isolate * isolate,Handle<JSObject> object,Handle<Object> value,size_t start_from,size_t length)4171 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate,
4172 Handle<JSObject> object,
4173 Handle<Object> value,
4174 size_t start_from, size_t length) {
4175 DCHECK(JSObject::PrototypeHasNoElements(isolate, *object));
4176 Handle<Map> original_map(object->map(), isolate);
4177 Handle<SloppyArgumentsElements> elements(
4178 SloppyArgumentsElements::cast(object->elements()), isolate);
4179
4180 for (size_t k = start_from; k < length; ++k) {
4181 DCHECK_EQ(object->map(), *original_map);
4182 InternalIndex entry =
4183 GetEntryForIndexImpl(isolate, *object, *elements, k, ALL_PROPERTIES);
4184 if (entry.is_not_found()) {
4185 continue;
4186 }
4187
4188 Handle<Object> element_k = Subclass::GetImpl(isolate, *elements, entry);
4189
4190 if (element_k->IsAccessorPair()) {
4191 LookupIterator it(isolate, object, k, LookupIterator::OWN);
4192 DCHECK(it.IsFound());
4193 DCHECK_EQ(it.state(), LookupIterator::ACCESSOR);
4194 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k,
4195 Object::GetPropertyWithAccessor(&it),
4196 Nothing<int64_t>());
4197
4198 if (value->StrictEquals(*element_k)) {
4199 return Just<int64_t>(k);
4200 }
4201
4202 if (object->map() != *original_map) {
4203 // Some mutation occurred in accessor. Abort "fast" path.
4204 return IndexOfValueSlowPath(isolate, object, value, k + 1, length);
4205 }
4206 } else if (value->StrictEquals(*element_k)) {
4207 return Just<int64_t>(k);
4208 }
4209 }
4210 return Just<int64_t>(-1);
4211 }
4212 };
4213
4214 class SlowSloppyArgumentsElementsAccessor
4215 : public SloppyArgumentsElementsAccessor<
4216 SlowSloppyArgumentsElementsAccessor, DictionaryElementsAccessor,
4217 ElementsKindTraits<SLOW_SLOPPY_ARGUMENTS_ELEMENTS>> {
4218 public:
ConvertArgumentsStoreResult(Isolate * isolate,Handle<SloppyArgumentsElements> elements,Handle<Object> result)4219 static Handle<Object> ConvertArgumentsStoreResult(
4220 Isolate* isolate, Handle<SloppyArgumentsElements> elements,
4221 Handle<Object> result) {
4222 // Elements of the arguments object in slow mode might be slow aliases.
4223 if (result->IsAliasedArgumentsEntry()) {
4224 DisallowHeapAllocation no_gc;
4225 AliasedArgumentsEntry alias = AliasedArgumentsEntry::cast(*result);
4226 Context context = elements->context();
4227 int context_entry = alias.aliased_context_slot();
4228 DCHECK(!context.get(context_entry).IsTheHole(isolate));
4229 return handle(context.get(context_entry), isolate);
4230 }
4231 return result;
4232 }
SloppyDeleteImpl(Handle<JSObject> obj,Handle<SloppyArgumentsElements> elements,InternalIndex entry)4233 static void SloppyDeleteImpl(Handle<JSObject> obj,
4234 Handle<SloppyArgumentsElements> elements,
4235 InternalIndex entry) {
4236 // No need to delete a context mapped entry from the arguments elements.
4237 if (entry.is_not_found()) return;
4238 Isolate* isolate = obj->GetIsolate();
4239 Handle<NumberDictionary> dict(NumberDictionary::cast(elements->arguments()),
4240 isolate);
4241 uint32_t length = elements->parameter_map_length();
4242 dict =
4243 NumberDictionary::DeleteEntry(isolate, dict, entry.adjust_down(length));
4244 elements->set_arguments(*dict);
4245 }
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)4246 static void AddImpl(Handle<JSObject> object, uint32_t index,
4247 Handle<Object> value, PropertyAttributes attributes,
4248 uint32_t new_capacity) {
4249 Isolate* isolate = object->GetIsolate();
4250 Handle<SloppyArgumentsElements> elements(
4251 SloppyArgumentsElements::cast(object->elements()), isolate);
4252 Handle<FixedArrayBase> old_arguments(
4253 FixedArrayBase::cast(elements->arguments()), isolate);
4254 Handle<NumberDictionary> dictionary =
4255 old_arguments->IsNumberDictionary()
4256 ? Handle<NumberDictionary>::cast(old_arguments)
4257 : JSObject::NormalizeElements(object);
4258 PropertyDetails details(kData, attributes, PropertyCellType::kNoCell);
4259 Handle<NumberDictionary> new_dictionary =
4260 NumberDictionary::Add(isolate, dictionary, index, value, details);
4261 if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
4262 if (*dictionary != *new_dictionary) {
4263 elements->set_arguments(*new_dictionary);
4264 }
4265 }
4266
ReconfigureImpl(Handle<JSObject> object,Handle<FixedArrayBase> store,InternalIndex entry,Handle<Object> value,PropertyAttributes attributes)4267 static void ReconfigureImpl(Handle<JSObject> object,
4268 Handle<FixedArrayBase> store, InternalIndex entry,
4269 Handle<Object> value,
4270 PropertyAttributes attributes) {
4271 Isolate* isolate = object->GetIsolate();
4272 Handle<SloppyArgumentsElements> elements =
4273 Handle<SloppyArgumentsElements>::cast(store);
4274 uint32_t length = elements->parameter_map_length();
4275 if (entry.as_uint32() < length) {
4276 Object probe = elements->get_mapped_entry(entry.as_uint32());
4277 DCHECK(!probe.IsTheHole(isolate));
4278 Context context = elements->context();
4279 int context_entry = Smi::ToInt(probe);
4280 DCHECK(!context.get(context_entry).IsTheHole(isolate));
4281 context.set(context_entry, *value);
4282
4283 // Redefining attributes of an aliased element destroys fast aliasing.
4284 elements->set_mapped_entry(entry.as_uint32(),
4285 ReadOnlyRoots(isolate).the_hole_value());
4286 // For elements that are still writable we re-establish slow aliasing.
4287 if ((attributes & READ_ONLY) == 0) {
4288 value = isolate->factory()->NewAliasedArgumentsEntry(context_entry);
4289 }
4290
4291 PropertyDetails details(kData, attributes, PropertyCellType::kNoCell);
4292 Handle<NumberDictionary> arguments(
4293 NumberDictionary::cast(elements->arguments()), isolate);
4294 arguments = NumberDictionary::Add(isolate, arguments, entry.as_uint32(),
4295 value, details);
4296 // If the attributes were NONE, we would have called set rather than
4297 // reconfigure.
4298 DCHECK_NE(NONE, attributes);
4299 object->RequireSlowElements(*arguments);
4300 elements->set_arguments(*arguments);
4301 } else {
4302 Handle<FixedArrayBase> arguments(elements->arguments(), isolate);
4303 DictionaryElementsAccessor::ReconfigureImpl(
4304 object, arguments, entry.adjust_down(length), value, attributes);
4305 }
4306 }
4307 };
4308
4309 class FastSloppyArgumentsElementsAccessor
4310 : public SloppyArgumentsElementsAccessor<
4311 FastSloppyArgumentsElementsAccessor, FastHoleyObjectElementsAccessor,
4312 ElementsKindTraits<FAST_SLOPPY_ARGUMENTS_ELEMENTS>> {
4313 public:
ConvertArgumentsStoreResult(Isolate * isolate,Handle<SloppyArgumentsElements> paramtere_map,Handle<Object> result)4314 static Handle<Object> ConvertArgumentsStoreResult(
4315 Isolate* isolate, Handle<SloppyArgumentsElements> paramtere_map,
4316 Handle<Object> result) {
4317 DCHECK(!result->IsAliasedArgumentsEntry());
4318 return result;
4319 }
4320
GetArguments(Isolate * isolate,FixedArrayBase store)4321 static Handle<FixedArray> GetArguments(Isolate* isolate,
4322 FixedArrayBase store) {
4323 SloppyArgumentsElements elements = SloppyArgumentsElements::cast(store);
4324 return Handle<FixedArray>(elements.arguments(), isolate);
4325 }
4326
NormalizeImpl(Handle<JSObject> object,Handle<FixedArrayBase> elements)4327 static Handle<NumberDictionary> NormalizeImpl(
4328 Handle<JSObject> object, Handle<FixedArrayBase> elements) {
4329 Handle<FixedArray> arguments =
4330 GetArguments(object->GetIsolate(), *elements);
4331 return FastHoleyObjectElementsAccessor::NormalizeImpl(object, arguments);
4332 }
4333
NormalizeArgumentsElements(Handle<JSObject> object,Handle<SloppyArgumentsElements> elements,InternalIndex * entry)4334 static Handle<NumberDictionary> NormalizeArgumentsElements(
4335 Handle<JSObject> object, Handle<SloppyArgumentsElements> elements,
4336 InternalIndex* entry) {
4337 Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(object);
4338 elements->set_arguments(*dictionary);
4339 // kMaxUInt32 indicates that a context mapped element got deleted. In this
4340 // case we only normalize the elements (aka. migrate to SLOW_SLOPPY).
4341 if (entry->is_not_found()) return dictionary;
4342 uint32_t length = elements->parameter_map_length();
4343 if (entry->as_uint32() >= length) {
4344 *entry =
4345 dictionary
4346 ->FindEntry(object->GetIsolate(), entry->as_uint32() - length)
4347 .adjust_up(length);
4348 }
4349 return dictionary;
4350 }
4351
SloppyDeleteImpl(Handle<JSObject> obj,Handle<SloppyArgumentsElements> elements,InternalIndex entry)4352 static void SloppyDeleteImpl(Handle<JSObject> obj,
4353 Handle<SloppyArgumentsElements> elements,
4354 InternalIndex entry) {
4355 // Always normalize element on deleting an entry.
4356 NormalizeArgumentsElements(obj, elements, &entry);
4357 SlowSloppyArgumentsElementsAccessor::SloppyDeleteImpl(obj, elements, entry);
4358 }
4359
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)4360 static void AddImpl(Handle<JSObject> object, uint32_t index,
4361 Handle<Object> value, PropertyAttributes attributes,
4362 uint32_t new_capacity) {
4363 DCHECK_EQ(NONE, attributes);
4364 Isolate* isolate = object->GetIsolate();
4365 Handle<SloppyArgumentsElements> elements(
4366 SloppyArgumentsElements::cast(object->elements()), isolate);
4367 Handle<FixedArray> old_arguments(elements->arguments(), isolate);
4368 if (old_arguments->IsNumberDictionary() ||
4369 static_cast<uint32_t>(old_arguments->length()) < new_capacity) {
4370 GrowCapacityAndConvertImpl(object, new_capacity);
4371 }
4372 FixedArray arguments = elements->arguments();
4373 // For fast holey objects, the entry equals the index. The code above made
4374 // sure that there's enough space to store the value. We cannot convert
4375 // index to entry explicitly since the slot still contains the hole, so the
4376 // current EntryForIndex would indicate that it is "absent" by returning
4377 // kMaxUInt32.
4378 FastHoleyObjectElementsAccessor::SetImpl(arguments, InternalIndex(index),
4379 *value);
4380 }
4381
ReconfigureImpl(Handle<JSObject> object,Handle<FixedArrayBase> store,InternalIndex entry,Handle<Object> value,PropertyAttributes attributes)4382 static void ReconfigureImpl(Handle<JSObject> object,
4383 Handle<FixedArrayBase> store, InternalIndex entry,
4384 Handle<Object> value,
4385 PropertyAttributes attributes) {
4386 DCHECK_EQ(object->elements(), *store);
4387 Handle<SloppyArgumentsElements> elements(
4388 SloppyArgumentsElements::cast(*store), object->GetIsolate());
4389 NormalizeArgumentsElements(object, elements, &entry);
4390 SlowSloppyArgumentsElementsAccessor::ReconfigureImpl(object, store, entry,
4391 value, attributes);
4392 }
4393
CopyElementsImpl(Isolate * isolate,FixedArrayBase from,uint32_t from_start,FixedArrayBase to,ElementsKind from_kind,uint32_t to_start,int packed_size,int copy_size)4394 static void CopyElementsImpl(Isolate* isolate, FixedArrayBase from,
4395 uint32_t from_start, FixedArrayBase to,
4396 ElementsKind from_kind, uint32_t to_start,
4397 int packed_size, int copy_size) {
4398 DCHECK(!to.IsNumberDictionary());
4399 if (from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS) {
4400 CopyDictionaryToObjectElements(isolate, from, from_start, to,
4401 HOLEY_ELEMENTS, to_start, copy_size);
4402 } else {
4403 DCHECK_EQ(FAST_SLOPPY_ARGUMENTS_ELEMENTS, from_kind);
4404 CopyObjectToObjectElements(isolate, from, HOLEY_ELEMENTS, from_start, to,
4405 HOLEY_ELEMENTS, to_start, copy_size);
4406 }
4407 }
4408
GrowCapacityAndConvertImpl(Handle<JSObject> object,uint32_t capacity)4409 static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
4410 uint32_t capacity) {
4411 Isolate* isolate = object->GetIsolate();
4412 Handle<SloppyArgumentsElements> elements(
4413 SloppyArgumentsElements::cast(object->elements()), isolate);
4414 Handle<FixedArray> old_arguments(FixedArray::cast(elements->arguments()),
4415 isolate);
4416 ElementsKind from_kind = object->GetElementsKind();
4417 // This method should only be called if there's a reason to update the
4418 // elements.
4419 DCHECK(from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS ||
4420 static_cast<uint32_t>(old_arguments->length()) < capacity);
4421 Handle<FixedArrayBase> arguments =
4422 ConvertElementsWithCapacity(object, old_arguments, from_kind, capacity);
4423 Handle<Map> new_map = JSObject::GetElementsTransitionMap(
4424 object, FAST_SLOPPY_ARGUMENTS_ELEMENTS);
4425 JSObject::MigrateToMap(isolate, object, new_map);
4426 elements->set_arguments(FixedArray::cast(*arguments));
4427 JSObject::ValidateElements(*object);
4428 }
4429 };
4430
4431 template <typename Subclass, typename BackingStoreAccessor, typename KindTraits>
4432 class StringWrapperElementsAccessor
4433 : public ElementsAccessorBase<Subclass, KindTraits> {
4434 public:
GetInternalImpl(Handle<JSObject> holder,InternalIndex entry)4435 static Handle<Object> GetInternalImpl(Handle<JSObject> holder,
4436 InternalIndex entry) {
4437 return GetImpl(holder, entry);
4438 }
4439
GetImpl(Handle<JSObject> holder,InternalIndex entry)4440 static Handle<Object> GetImpl(Handle<JSObject> holder, InternalIndex entry) {
4441 Isolate* isolate = holder->GetIsolate();
4442 Handle<String> string(GetString(*holder), isolate);
4443 uint32_t length = static_cast<uint32_t>(string->length());
4444 if (entry.as_uint32() < length) {
4445 return isolate->factory()->LookupSingleCharacterStringFromCode(
4446 String::Flatten(isolate, string)->Get(entry.as_int()));
4447 }
4448 return BackingStoreAccessor::GetImpl(isolate, holder->elements(),
4449 entry.adjust_down(length));
4450 }
4451
GetImpl(Isolate * isolate,FixedArrayBase elements,InternalIndex entry)4452 static Handle<Object> GetImpl(Isolate* isolate, FixedArrayBase elements,
4453 InternalIndex entry) {
4454 UNREACHABLE();
4455 }
4456
GetDetailsImpl(JSObject holder,InternalIndex entry)4457 static PropertyDetails GetDetailsImpl(JSObject holder, InternalIndex entry) {
4458 uint32_t length = static_cast<uint32_t>(GetString(holder).length());
4459 if (entry.as_uint32() < length) {
4460 PropertyAttributes attributes =
4461 static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
4462 return PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
4463 }
4464 return BackingStoreAccessor::GetDetailsImpl(holder,
4465 entry.adjust_down(length));
4466 }
4467
GetEntryForIndexImpl(Isolate * isolate,JSObject holder,FixedArrayBase backing_store,size_t index,PropertyFilter filter)4468 static InternalIndex GetEntryForIndexImpl(Isolate* isolate, JSObject holder,
4469 FixedArrayBase backing_store,
4470 size_t index,
4471 PropertyFilter filter) {
4472 uint32_t length = static_cast<uint32_t>(GetString(holder).length());
4473 if (index < length) return InternalIndex(index);
4474 InternalIndex backing_store_entry =
4475 BackingStoreAccessor::GetEntryForIndexImpl(
4476 isolate, holder, backing_store, index, filter);
4477 if (backing_store_entry.is_not_found()) return backing_store_entry;
4478 return backing_store_entry.adjust_up(length);
4479 }
4480
DeleteImpl(Handle<JSObject> holder,InternalIndex entry)4481 static void DeleteImpl(Handle<JSObject> holder, InternalIndex entry) {
4482 uint32_t length = static_cast<uint32_t>(GetString(*holder).length());
4483 if (entry.as_uint32() < length) {
4484 return; // String contents can't be deleted.
4485 }
4486 BackingStoreAccessor::DeleteImpl(holder, entry.adjust_down(length));
4487 }
4488
SetImpl(Handle<JSObject> holder,InternalIndex entry,Object value)4489 static void SetImpl(Handle<JSObject> holder, InternalIndex entry,
4490 Object value) {
4491 uint32_t length = static_cast<uint32_t>(GetString(*holder).length());
4492 if (entry.as_uint32() < length) {
4493 return; // String contents are read-only.
4494 }
4495 BackingStoreAccessor::SetImpl(holder->elements(), entry.adjust_down(length),
4496 value);
4497 }
4498
AddImpl(Handle<JSObject> object,uint32_t index,Handle<Object> value,PropertyAttributes attributes,uint32_t new_capacity)4499 static void AddImpl(Handle<JSObject> object, uint32_t index,
4500 Handle<Object> value, PropertyAttributes attributes,
4501 uint32_t new_capacity) {
4502 DCHECK(index >= static_cast<uint32_t>(GetString(*object).length()));
4503 // Explicitly grow fast backing stores if needed. Dictionaries know how to
4504 // extend their capacity themselves.
4505 if (KindTraits::Kind == FAST_STRING_WRAPPER_ELEMENTS &&
4506 (object->GetElementsKind() == SLOW_STRING_WRAPPER_ELEMENTS ||
4507 BackingStoreAccessor::GetCapacityImpl(*object, object->elements()) !=
4508 new_capacity)) {
4509 GrowCapacityAndConvertImpl(object, new_capacity);
4510 }
4511 BackingStoreAccessor::AddImpl(object, index, value, attributes,
4512 new_capacity);
4513 }
4514
ReconfigureImpl(Handle<JSObject> object,Handle<FixedArrayBase> store,InternalIndex entry,Handle<Object> value,PropertyAttributes attributes)4515 static void ReconfigureImpl(Handle<JSObject> object,
4516 Handle<FixedArrayBase> store, InternalIndex entry,
4517 Handle<Object> value,
4518 PropertyAttributes attributes) {
4519 uint32_t length = static_cast<uint32_t>(GetString(*object).length());
4520 if (entry.as_uint32() < length) {
4521 return; // String contents can't be reconfigured.
4522 }
4523 BackingStoreAccessor::ReconfigureImpl(
4524 object, store, entry.adjust_down(length), value, attributes);
4525 }
4526
AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,KeyAccumulator * accumulator,AddKeyConversion convert)4527 V8_WARN_UNUSED_RESULT static ExceptionStatus AddElementsToKeyAccumulatorImpl(
4528 Handle<JSObject> receiver, KeyAccumulator* accumulator,
4529 AddKeyConversion convert) {
4530 Isolate* isolate = receiver->GetIsolate();
4531 Handle<String> string(GetString(*receiver), isolate);
4532 string = String::Flatten(isolate, string);
4533 uint32_t length = static_cast<uint32_t>(string->length());
4534 for (uint32_t i = 0; i < length; i++) {
4535 Handle<String> key =
4536 isolate->factory()->LookupSingleCharacterStringFromCode(
4537 string->Get(i));
4538 RETURN_FAILURE_IF_NOT_SUCCESSFUL(accumulator->AddKey(key, convert));
4539 }
4540 return BackingStoreAccessor::AddElementsToKeyAccumulatorImpl(
4541 receiver, accumulator, convert);
4542 }
4543
CollectElementIndicesImpl(Handle<JSObject> object,Handle<FixedArrayBase> backing_store,KeyAccumulator * keys)4544 V8_WARN_UNUSED_RESULT static ExceptionStatus CollectElementIndicesImpl(
4545 Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
4546 KeyAccumulator* keys) {
4547 uint32_t length = GetString(*object).length();
4548 Factory* factory = keys->isolate()->factory();
4549 for (uint32_t i = 0; i < length; i++) {
4550 RETURN_FAILURE_IF_NOT_SUCCESSFUL(
4551 keys->AddKey(factory->NewNumberFromUint(i)));
4552 }
4553 return BackingStoreAccessor::CollectElementIndicesImpl(object,
4554 backing_store, keys);
4555 }
4556
GrowCapacityAndConvertImpl(Handle<JSObject> object,uint32_t capacity)4557 static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
4558 uint32_t capacity) {
4559 Handle<FixedArrayBase> old_elements(object->elements(),
4560 object->GetIsolate());
4561 ElementsKind from_kind = object->GetElementsKind();
4562 if (from_kind == FAST_STRING_WRAPPER_ELEMENTS) {
4563 // The optimizing compiler relies on the prototype lookups of String
4564 // objects always returning undefined. If there's a store to the
4565 // initial String.prototype object, make sure all the optimizations
4566 // are invalidated.
4567 object->GetIsolate()->UpdateNoElementsProtectorOnSetLength(object);
4568 }
4569 // This method should only be called if there's a reason to update the
4570 // elements.
4571 DCHECK(from_kind == SLOW_STRING_WRAPPER_ELEMENTS ||
4572 static_cast<uint32_t>(old_elements->length()) < capacity);
4573 Subclass::BasicGrowCapacityAndConvertImpl(object, old_elements, from_kind,
4574 FAST_STRING_WRAPPER_ELEMENTS,
4575 capacity);
4576 }
4577
CopyElementsImpl(Isolate * isolate,FixedArrayBase from,uint32_t from_start,FixedArrayBase to,ElementsKind from_kind,uint32_t to_start,int packed_size,int copy_size)4578 static void CopyElementsImpl(Isolate* isolate, FixedArrayBase from,
4579 uint32_t from_start, FixedArrayBase to,
4580 ElementsKind from_kind, uint32_t to_start,
4581 int packed_size, int copy_size) {
4582 DCHECK(!to.IsNumberDictionary());
4583 if (from_kind == SLOW_STRING_WRAPPER_ELEMENTS) {
4584 CopyDictionaryToObjectElements(isolate, from, from_start, to,
4585 HOLEY_ELEMENTS, to_start, copy_size);
4586 } else {
4587 DCHECK_EQ(FAST_STRING_WRAPPER_ELEMENTS, from_kind);
4588 CopyObjectToObjectElements(isolate, from, HOLEY_ELEMENTS, from_start, to,
4589 HOLEY_ELEMENTS, to_start, copy_size);
4590 }
4591 }
4592
NumberOfElementsImpl(JSObject object,FixedArrayBase backing_store)4593 static uint32_t NumberOfElementsImpl(JSObject object,
4594 FixedArrayBase backing_store) {
4595 uint32_t length = GetString(object).length();
4596 return length +
4597 BackingStoreAccessor::NumberOfElementsImpl(object, backing_store);
4598 }
4599
4600 private:
GetString(JSObject holder)4601 static String GetString(JSObject holder) {
4602 DCHECK(holder.IsJSPrimitiveWrapper());
4603 JSPrimitiveWrapper js_value = JSPrimitiveWrapper::cast(holder);
4604 DCHECK(js_value.value().IsString());
4605 return String::cast(js_value.value());
4606 }
4607 };
4608
4609 class FastStringWrapperElementsAccessor
4610 : public StringWrapperElementsAccessor<
4611 FastStringWrapperElementsAccessor, FastHoleyObjectElementsAccessor,
4612 ElementsKindTraits<FAST_STRING_WRAPPER_ELEMENTS>> {
4613 public:
NormalizeImpl(Handle<JSObject> object,Handle<FixedArrayBase> elements)4614 static Handle<NumberDictionary> NormalizeImpl(
4615 Handle<JSObject> object, Handle<FixedArrayBase> elements) {
4616 return FastHoleyObjectElementsAccessor::NormalizeImpl(object, elements);
4617 }
4618 };
4619
4620 class SlowStringWrapperElementsAccessor
4621 : public StringWrapperElementsAccessor<
4622 SlowStringWrapperElementsAccessor, DictionaryElementsAccessor,
4623 ElementsKindTraits<SLOW_STRING_WRAPPER_ELEMENTS>> {
4624 public:
HasAccessorsImpl(JSObject holder,FixedArrayBase backing_store)4625 static bool HasAccessorsImpl(JSObject holder, FixedArrayBase backing_store) {
4626 return DictionaryElementsAccessor::HasAccessorsImpl(holder, backing_store);
4627 }
4628 };
4629
4630 } // namespace
4631
ArrayConstructInitializeElements(Handle<JSArray> array,JavaScriptArguments * args)4632 MaybeHandle<Object> ArrayConstructInitializeElements(
4633 Handle<JSArray> array, JavaScriptArguments* args) {
4634 if (args->length() == 0) {
4635 // Optimize the case where there are no parameters passed.
4636 JSArray::Initialize(array, JSArray::kPreallocatedArrayElements);
4637 return array;
4638
4639 } else if (args->length() == 1 && args->at(0)->IsNumber()) {
4640 uint32_t length;
4641 if (!args->at(0)->ToArrayLength(&length)) {
4642 return ThrowArrayLengthRangeError(array->GetIsolate());
4643 }
4644
4645 // Optimize the case where there is one argument and the argument is a small
4646 // smi.
4647 if (length > 0 && length < JSArray::kInitialMaxFastElementArray) {
4648 ElementsKind elements_kind = array->GetElementsKind();
4649 JSArray::Initialize(array, length, length);
4650
4651 if (!IsHoleyElementsKind(elements_kind)) {
4652 elements_kind = GetHoleyElementsKind(elements_kind);
4653 JSObject::TransitionElementsKind(array, elements_kind);
4654 }
4655 } else if (length == 0) {
4656 JSArray::Initialize(array, JSArray::kPreallocatedArrayElements);
4657 } else {
4658 // Take the argument as the length.
4659 JSArray::Initialize(array, 0);
4660 JSArray::SetLength(array, length);
4661 }
4662 return array;
4663 }
4664
4665 Factory* factory = array->GetIsolate()->factory();
4666
4667 // Set length and elements on the array.
4668 int number_of_elements = args->length();
4669 JSObject::EnsureCanContainElements(array, args, number_of_elements,
4670 ALLOW_CONVERTED_DOUBLE_ELEMENTS);
4671
4672 // Allocate an appropriately typed elements array.
4673 ElementsKind elements_kind = array->GetElementsKind();
4674 Handle<FixedArrayBase> elms;
4675 if (IsDoubleElementsKind(elements_kind)) {
4676 elms = Handle<FixedArrayBase>::cast(
4677 factory->NewFixedDoubleArray(number_of_elements));
4678 } else {
4679 elms = Handle<FixedArrayBase>::cast(
4680 factory->NewFixedArrayWithHoles(number_of_elements));
4681 }
4682
4683 // Fill in the content
4684 switch (elements_kind) {
4685 case HOLEY_SMI_ELEMENTS:
4686 case PACKED_SMI_ELEMENTS: {
4687 Handle<FixedArray> smi_elms = Handle<FixedArray>::cast(elms);
4688 for (int entry = 0; entry < number_of_elements; entry++) {
4689 smi_elms->set(entry, (*args)[entry], SKIP_WRITE_BARRIER);
4690 }
4691 break;
4692 }
4693 case HOLEY_ELEMENTS:
4694 case PACKED_ELEMENTS: {
4695 DisallowHeapAllocation no_gc;
4696 WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
4697 Handle<FixedArray> object_elms = Handle<FixedArray>::cast(elms);
4698 for (int entry = 0; entry < number_of_elements; entry++) {
4699 object_elms->set(entry, (*args)[entry], mode);
4700 }
4701 break;
4702 }
4703 case HOLEY_DOUBLE_ELEMENTS:
4704 case PACKED_DOUBLE_ELEMENTS: {
4705 Handle<FixedDoubleArray> double_elms =
4706 Handle<FixedDoubleArray>::cast(elms);
4707 for (int entry = 0; entry < number_of_elements; entry++) {
4708 double_elms->set(entry, (*args)[entry].Number());
4709 }
4710 break;
4711 }
4712 default:
4713 UNREACHABLE();
4714 }
4715
4716 array->set_elements(*elms);
4717 array->set_length(Smi::FromInt(number_of_elements));
4718 return array;
4719 }
4720
CopyFastNumberJSArrayElementsToTypedArray(Address raw_context,Address raw_source,Address raw_destination,uintptr_t length,uintptr_t offset)4721 void CopyFastNumberJSArrayElementsToTypedArray(Address raw_context,
4722 Address raw_source,
4723 Address raw_destination,
4724 uintptr_t length,
4725 uintptr_t offset) {
4726 Context context = Context::cast(Object(raw_context));
4727 JSArray source = JSArray::cast(Object(raw_source));
4728 JSTypedArray destination = JSTypedArray::cast(Object(raw_destination));
4729
4730 switch (destination.GetElementsKind()) {
4731 #define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype) \
4732 case TYPE##_ELEMENTS: \
4733 CHECK(Type##ElementsAccessor::TryCopyElementsFastNumber( \
4734 context, source, destination, length, offset)); \
4735 break;
4736 TYPED_ARRAYS(TYPED_ARRAYS_CASE)
4737 #undef TYPED_ARRAYS_CASE
4738 default:
4739 UNREACHABLE();
4740 }
4741 }
4742
CopyTypedArrayElementsToTypedArray(Address raw_source,Address raw_destination,uintptr_t length,uintptr_t offset)4743 void CopyTypedArrayElementsToTypedArray(Address raw_source,
4744 Address raw_destination,
4745 uintptr_t length, uintptr_t offset) {
4746 JSTypedArray source = JSTypedArray::cast(Object(raw_source));
4747 JSTypedArray destination = JSTypedArray::cast(Object(raw_destination));
4748
4749 switch (destination.GetElementsKind()) {
4750 #define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype) \
4751 case TYPE##_ELEMENTS: \
4752 Type##ElementsAccessor::CopyElementsFromTypedArray(source, destination, \
4753 length, offset); \
4754 break;
4755 TYPED_ARRAYS(TYPED_ARRAYS_CASE)
4756 #undef TYPED_ARRAYS_CASE
4757 default:
4758 UNREACHABLE();
4759 }
4760 }
4761
CopyTypedArrayElementsSlice(Address raw_source,Address raw_destination,uintptr_t start,uintptr_t end)4762 void CopyTypedArrayElementsSlice(Address raw_source, Address raw_destination,
4763 uintptr_t start, uintptr_t end) {
4764 JSTypedArray source = JSTypedArray::cast(Object(raw_source));
4765 JSTypedArray destination = JSTypedArray::cast(Object(raw_destination));
4766
4767 destination.GetElementsAccessor()->CopyTypedArrayElementsSlice(
4768 source, destination, start, end);
4769 }
4770
InitializeOncePerProcess()4771 void ElementsAccessor::InitializeOncePerProcess() {
4772 static ElementsAccessor* accessor_array[] = {
4773 #define ACCESSOR_ARRAY(Class, Kind, Store) new Class(),
4774 ELEMENTS_LIST(ACCESSOR_ARRAY)
4775 #undef ACCESSOR_ARRAY
4776 };
4777
4778 STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) ==
4779 kElementsKindCount);
4780
4781 elements_accessors_ = accessor_array;
4782 }
4783
TearDown()4784 void ElementsAccessor::TearDown() {
4785 if (elements_accessors_ == nullptr) return;
4786 #define ACCESSOR_DELETE(Class, Kind, Store) delete elements_accessors_[Kind];
4787 ELEMENTS_LIST(ACCESSOR_DELETE)
4788 #undef ACCESSOR_DELETE
4789 elements_accessors_ = nullptr;
4790 }
4791
Concat(Isolate * isolate,BuiltinArguments * args,uint32_t concat_size,uint32_t result_len)4792 Handle<JSArray> ElementsAccessor::Concat(Isolate* isolate,
4793 BuiltinArguments* args,
4794 uint32_t concat_size,
4795 uint32_t result_len) {
4796 ElementsKind result_elements_kind = GetInitialFastElementsKind();
4797 bool has_raw_doubles = false;
4798 {
4799 DisallowHeapAllocation no_gc;
4800 bool is_holey = false;
4801 for (uint32_t i = 0; i < concat_size; i++) {
4802 Object arg = (*args)[i];
4803 ElementsKind arg_kind = JSArray::cast(arg).GetElementsKind();
4804 has_raw_doubles = has_raw_doubles || IsDoubleElementsKind(arg_kind);
4805 is_holey = is_holey || IsHoleyElementsKind(arg_kind);
4806 result_elements_kind =
4807 GetMoreGeneralElementsKind(result_elements_kind, arg_kind);
4808 }
4809 if (is_holey) {
4810 result_elements_kind = GetHoleyElementsKind(result_elements_kind);
4811 }
4812 }
4813
4814 // If a double array is concatted into a fast elements array, the fast
4815 // elements array needs to be initialized to contain proper holes, since
4816 // boxing doubles may cause incremental marking.
4817 bool requires_double_boxing =
4818 has_raw_doubles && !IsDoubleElementsKind(result_elements_kind);
4819 ArrayStorageAllocationMode mode = requires_double_boxing
4820 ? INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE
4821 : DONT_INITIALIZE_ARRAY_ELEMENTS;
4822 Handle<JSArray> result_array = isolate->factory()->NewJSArray(
4823 result_elements_kind, result_len, result_len, mode);
4824 if (result_len == 0) return result_array;
4825
4826 uint32_t insertion_index = 0;
4827 Handle<FixedArrayBase> storage(result_array->elements(), isolate);
4828 ElementsAccessor* accessor = ElementsAccessor::ForKind(result_elements_kind);
4829 for (uint32_t i = 0; i < concat_size; i++) {
4830 // It is crucial to keep |array| in a raw pointer form to avoid
4831 // performance degradation.
4832 JSArray array = JSArray::cast((*args)[i]);
4833 uint32_t len = 0;
4834 array.length().ToArrayLength(&len);
4835 if (len == 0) continue;
4836 ElementsKind from_kind = array.GetElementsKind();
4837 accessor->CopyElements(array, 0, from_kind, storage, insertion_index, len);
4838 insertion_index += len;
4839 }
4840
4841 DCHECK_EQ(insertion_index, result_len);
4842 return result_array;
4843 }
4844
4845 ElementsAccessor** ElementsAccessor::elements_accessors_ = nullptr;
4846
4847 #undef ELEMENTS_LIST
4848 #undef RETURN_NOTHING_IF_NOT_SUCCESSFUL
4849 #undef RETURN_FAILURE_IF_NOT_SUCCESSFUL
4850 } // namespace internal
4851 } // namespace v8
4852