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/elements.h" 6 7 #include "src/arguments.h" 8 #include "src/conversions.h" 9 #include "src/factory.h" 10 #include "src/isolate-inl.h" 11 #include "src/messages.h" 12 #include "src/objects-inl.h" 13 #include "src/utils.h" 14 15 // Each concrete ElementsAccessor can handle exactly one ElementsKind, 16 // several abstract ElementsAccessor classes are used to allow sharing 17 // common code. 18 // 19 // Inheritance hierarchy: 20 // - ElementsAccessorBase (abstract) 21 // - FastElementsAccessor (abstract) 22 // - FastSmiOrObjectElementsAccessor 23 // - FastPackedSmiElementsAccessor 24 // - FastHoleySmiElementsAccessor 25 // - FastPackedObjectElementsAccessor 26 // - FastHoleyObjectElementsAccessor 27 // - FastDoubleElementsAccessor 28 // - FastPackedDoubleElementsAccessor 29 // - FastHoleyDoubleElementsAccessor 30 // - TypedElementsAccessor: template, with instantiations: 31 // - FixedUint8ElementsAccessor 32 // - FixedInt8ElementsAccessor 33 // - FixedUint16ElementsAccessor 34 // - FixedInt16ElementsAccessor 35 // - FixedUint32ElementsAccessor 36 // - FixedInt32ElementsAccessor 37 // - FixedFloat32ElementsAccessor 38 // - FixedFloat64ElementsAccessor 39 // - FixedUint8ClampedElementsAccessor 40 // - DictionaryElementsAccessor 41 // - SloppyArgumentsElementsAccessor 42 // - FastSloppyArgumentsElementsAccessor 43 // - SlowSloppyArgumentsElementsAccessor 44 // - StringWrapperElementsAccessor 45 // - FastStringWrapperElementsAccessor 46 // - SlowStringWrapperElementsAccessor 47 48 namespace v8 { 49 namespace internal { 50 51 52 namespace { 53 54 55 static const int kPackedSizeNotKnown = -1; 56 57 enum Where { AT_START, AT_END }; 58 59 60 // First argument in list is the accessor class, the second argument is the 61 // accessor ElementsKind, and the third is the backing store class. Use the 62 // fast element handler for smi-only arrays. The implementation is currently 63 // identical. Note that the order must match that of the ElementsKind enum for 64 // the |accessor_array[]| below to work. 65 #define ELEMENTS_LIST(V) \ 66 V(FastPackedSmiElementsAccessor, FAST_SMI_ELEMENTS, FixedArray) \ 67 V(FastHoleySmiElementsAccessor, FAST_HOLEY_SMI_ELEMENTS, FixedArray) \ 68 V(FastPackedObjectElementsAccessor, FAST_ELEMENTS, FixedArray) \ 69 V(FastHoleyObjectElementsAccessor, FAST_HOLEY_ELEMENTS, FixedArray) \ 70 V(FastPackedDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS, FixedDoubleArray) \ 71 V(FastHoleyDoubleElementsAccessor, FAST_HOLEY_DOUBLE_ELEMENTS, \ 72 FixedDoubleArray) \ 73 V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, SeededNumberDictionary) \ 74 V(FastSloppyArgumentsElementsAccessor, FAST_SLOPPY_ARGUMENTS_ELEMENTS, \ 75 FixedArray) \ 76 V(SlowSloppyArgumentsElementsAccessor, SLOW_SLOPPY_ARGUMENTS_ELEMENTS, \ 77 FixedArray) \ 78 V(FastStringWrapperElementsAccessor, FAST_STRING_WRAPPER_ELEMENTS, \ 79 FixedArray) \ 80 V(SlowStringWrapperElementsAccessor, SLOW_STRING_WRAPPER_ELEMENTS, \ 81 FixedArray) \ 82 V(FixedUint8ElementsAccessor, UINT8_ELEMENTS, FixedUint8Array) \ 83 V(FixedInt8ElementsAccessor, INT8_ELEMENTS, FixedInt8Array) \ 84 V(FixedUint16ElementsAccessor, UINT16_ELEMENTS, FixedUint16Array) \ 85 V(FixedInt16ElementsAccessor, INT16_ELEMENTS, FixedInt16Array) \ 86 V(FixedUint32ElementsAccessor, UINT32_ELEMENTS, FixedUint32Array) \ 87 V(FixedInt32ElementsAccessor, INT32_ELEMENTS, FixedInt32Array) \ 88 V(FixedFloat32ElementsAccessor, FLOAT32_ELEMENTS, FixedFloat32Array) \ 89 V(FixedFloat64ElementsAccessor, FLOAT64_ELEMENTS, FixedFloat64Array) \ 90 V(FixedUint8ClampedElementsAccessor, UINT8_CLAMPED_ELEMENTS, \ 91 FixedUint8ClampedArray) 92 93 template<ElementsKind Kind> class ElementsKindTraits { 94 public: 95 typedef FixedArrayBase BackingStore; 96 }; 97 98 #define ELEMENTS_TRAITS(Class, KindParam, Store) \ 99 template<> class ElementsKindTraits<KindParam> { \ 100 public: /* NOLINT */ \ 101 static const ElementsKind Kind = KindParam; \ 102 typedef Store BackingStore; \ 103 }; 104 ELEMENTS_LIST(ELEMENTS_TRAITS) 105 #undef ELEMENTS_TRAITS 106 107 108 MUST_USE_RESULT 109 MaybeHandle<Object> ThrowArrayLengthRangeError(Isolate* isolate) { 110 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidArrayLength), 111 Object); 112 } 113 114 115 void CopyObjectToObjectElements(FixedArrayBase* from_base, 116 ElementsKind from_kind, uint32_t from_start, 117 FixedArrayBase* to_base, ElementsKind to_kind, 118 uint32_t to_start, int raw_copy_size) { 119 DCHECK(to_base->map() != 120 from_base->GetIsolate()->heap()->fixed_cow_array_map()); 121 DisallowHeapAllocation no_allocation; 122 int copy_size = raw_copy_size; 123 if (raw_copy_size < 0) { 124 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 125 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 126 copy_size = Min(from_base->length() - from_start, 127 to_base->length() - to_start); 128 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 129 int start = to_start + copy_size; 130 int length = to_base->length() - start; 131 if (length > 0) { 132 Heap* heap = from_base->GetHeap(); 133 MemsetPointer(FixedArray::cast(to_base)->data_start() + start, 134 heap->the_hole_value(), length); 135 } 136 } 137 } 138 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 139 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 140 if (copy_size == 0) return; 141 FixedArray* from = FixedArray::cast(from_base); 142 FixedArray* to = FixedArray::cast(to_base); 143 DCHECK(IsFastSmiOrObjectElementsKind(from_kind)); 144 DCHECK(IsFastSmiOrObjectElementsKind(to_kind)); 145 146 WriteBarrierMode write_barrier_mode = 147 (IsFastObjectElementsKind(from_kind) && IsFastObjectElementsKind(to_kind)) 148 ? UPDATE_WRITE_BARRIER 149 : SKIP_WRITE_BARRIER; 150 for (int i = 0; i < copy_size; i++) { 151 Object* value = from->get(from_start + i); 152 to->set(to_start + i, value, write_barrier_mode); 153 } 154 } 155 156 157 static void CopyDictionaryToObjectElements( 158 FixedArrayBase* from_base, uint32_t from_start, FixedArrayBase* to_base, 159 ElementsKind to_kind, uint32_t to_start, int raw_copy_size) { 160 DisallowHeapAllocation no_allocation; 161 SeededNumberDictionary* from = SeededNumberDictionary::cast(from_base); 162 int copy_size = raw_copy_size; 163 if (raw_copy_size < 0) { 164 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 165 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 166 copy_size = from->max_number_key() + 1 - from_start; 167 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 168 int start = to_start + copy_size; 169 int length = to_base->length() - start; 170 if (length > 0) { 171 Heap* heap = from->GetHeap(); 172 MemsetPointer(FixedArray::cast(to_base)->data_start() + start, 173 heap->the_hole_value(), length); 174 } 175 } 176 } 177 DCHECK(to_base != from_base); 178 DCHECK(IsFastSmiOrObjectElementsKind(to_kind)); 179 if (copy_size == 0) return; 180 FixedArray* to = FixedArray::cast(to_base); 181 uint32_t to_length = to->length(); 182 if (to_start + copy_size > to_length) { 183 copy_size = to_length - to_start; 184 } 185 WriteBarrierMode write_barrier_mode = IsFastObjectElementsKind(to_kind) 186 ? UPDATE_WRITE_BARRIER 187 : SKIP_WRITE_BARRIER; 188 for (int i = 0; i < copy_size; i++) { 189 int entry = from->FindEntry(i + from_start); 190 if (entry != SeededNumberDictionary::kNotFound) { 191 Object* value = from->ValueAt(entry); 192 DCHECK(!value->IsTheHole(from->GetIsolate())); 193 to->set(i + to_start, value, write_barrier_mode); 194 } else { 195 to->set_the_hole(i + to_start); 196 } 197 } 198 } 199 200 201 // NOTE: this method violates the handlified function signature convention: 202 // raw pointer parameters in the function that allocates. 203 // See ElementsAccessorBase::CopyElements() for details. 204 static void CopyDoubleToObjectElements(FixedArrayBase* from_base, 205 uint32_t from_start, 206 FixedArrayBase* to_base, 207 uint32_t to_start, int raw_copy_size) { 208 int copy_size = raw_copy_size; 209 if (raw_copy_size < 0) { 210 DisallowHeapAllocation no_allocation; 211 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 212 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 213 copy_size = Min(from_base->length() - from_start, 214 to_base->length() - to_start); 215 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 216 // Also initialize the area that will be copied over since HeapNumber 217 // allocation below can cause an incremental marking step, requiring all 218 // existing heap objects to be propertly initialized. 219 int start = to_start; 220 int length = to_base->length() - start; 221 if (length > 0) { 222 Heap* heap = from_base->GetHeap(); 223 MemsetPointer(FixedArray::cast(to_base)->data_start() + start, 224 heap->the_hole_value(), length); 225 } 226 } 227 } 228 229 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 230 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 231 if (copy_size == 0) return; 232 233 // From here on, the code below could actually allocate. Therefore the raw 234 // values are wrapped into handles. 235 Isolate* isolate = from_base->GetIsolate(); 236 Handle<FixedDoubleArray> from(FixedDoubleArray::cast(from_base), isolate); 237 Handle<FixedArray> to(FixedArray::cast(to_base), isolate); 238 239 // Use an outer loop to not waste too much time on creating HandleScopes. 240 // On the other hand we might overflow a single handle scope depending on 241 // the copy_size. 242 int offset = 0; 243 while (offset < copy_size) { 244 HandleScope scope(isolate); 245 offset += 100; 246 for (int i = offset - 100; i < offset && i < copy_size; ++i) { 247 Handle<Object> value = 248 FixedDoubleArray::get(*from, i + from_start, isolate); 249 to->set(i + to_start, *value, UPDATE_WRITE_BARRIER); 250 } 251 } 252 } 253 254 255 static void CopyDoubleToDoubleElements(FixedArrayBase* from_base, 256 uint32_t from_start, 257 FixedArrayBase* to_base, 258 uint32_t to_start, int raw_copy_size) { 259 DisallowHeapAllocation no_allocation; 260 int copy_size = raw_copy_size; 261 if (raw_copy_size < 0) { 262 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 263 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 264 copy_size = Min(from_base->length() - from_start, 265 to_base->length() - to_start); 266 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 267 for (int i = to_start + copy_size; i < to_base->length(); ++i) { 268 FixedDoubleArray::cast(to_base)->set_the_hole(i); 269 } 270 } 271 } 272 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 273 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 274 if (copy_size == 0) return; 275 FixedDoubleArray* from = FixedDoubleArray::cast(from_base); 276 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 277 Address to_address = to->address() + FixedDoubleArray::kHeaderSize; 278 Address from_address = from->address() + FixedDoubleArray::kHeaderSize; 279 to_address += kDoubleSize * to_start; 280 from_address += kDoubleSize * from_start; 281 int words_per_double = (kDoubleSize / kPointerSize); 282 CopyWords(reinterpret_cast<Object**>(to_address), 283 reinterpret_cast<Object**>(from_address), 284 static_cast<size_t>(words_per_double * copy_size)); 285 } 286 287 288 static void CopySmiToDoubleElements(FixedArrayBase* from_base, 289 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(raw_copy_size == ElementsAccessor::kCopyToEnd || 296 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 297 copy_size = from_base->length() - from_start; 298 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 299 for (int i = to_start + copy_size; i < to_base->length(); ++i) { 300 FixedDoubleArray::cast(to_base)->set_the_hole(i); 301 } 302 } 303 } 304 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 305 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 306 if (copy_size == 0) return; 307 FixedArray* from = FixedArray::cast(from_base); 308 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 309 Object* the_hole = from->GetHeap()->the_hole_value(); 310 for (uint32_t from_end = from_start + static_cast<uint32_t>(copy_size); 311 from_start < from_end; from_start++, to_start++) { 312 Object* hole_or_smi = from->get(from_start); 313 if (hole_or_smi == the_hole) { 314 to->set_the_hole(to_start); 315 } else { 316 to->set(to_start, Smi::cast(hole_or_smi)->value()); 317 } 318 } 319 } 320 321 322 static void CopyPackedSmiToDoubleElements(FixedArrayBase* from_base, 323 uint32_t from_start, 324 FixedArrayBase* to_base, 325 uint32_t to_start, int packed_size, 326 int raw_copy_size) { 327 DisallowHeapAllocation no_allocation; 328 int copy_size = raw_copy_size; 329 uint32_t to_end; 330 if (raw_copy_size < 0) { 331 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 332 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 333 copy_size = packed_size - from_start; 334 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 335 to_end = to_base->length(); 336 for (uint32_t i = to_start + copy_size; i < to_end; ++i) { 337 FixedDoubleArray::cast(to_base)->set_the_hole(i); 338 } 339 } else { 340 to_end = to_start + static_cast<uint32_t>(copy_size); 341 } 342 } else { 343 to_end = to_start + static_cast<uint32_t>(copy_size); 344 } 345 DCHECK(static_cast<int>(to_end) <= to_base->length()); 346 DCHECK(packed_size >= 0 && packed_size <= copy_size); 347 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 348 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 349 if (copy_size == 0) return; 350 FixedArray* from = FixedArray::cast(from_base); 351 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 352 for (uint32_t from_end = from_start + static_cast<uint32_t>(packed_size); 353 from_start < from_end; from_start++, to_start++) { 354 Object* smi = from->get(from_start); 355 DCHECK(!smi->IsTheHole(from->GetIsolate())); 356 to->set(to_start, Smi::cast(smi)->value()); 357 } 358 } 359 360 361 static void CopyObjectToDoubleElements(FixedArrayBase* from_base, 362 uint32_t from_start, 363 FixedArrayBase* to_base, 364 uint32_t to_start, int raw_copy_size) { 365 DisallowHeapAllocation no_allocation; 366 int copy_size = raw_copy_size; 367 if (raw_copy_size < 0) { 368 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 369 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 370 copy_size = from_base->length() - from_start; 371 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 372 for (int i = to_start + copy_size; i < to_base->length(); ++i) { 373 FixedDoubleArray::cast(to_base)->set_the_hole(i); 374 } 375 } 376 } 377 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 378 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 379 if (copy_size == 0) return; 380 FixedArray* from = FixedArray::cast(from_base); 381 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 382 Object* the_hole = from->GetHeap()->the_hole_value(); 383 for (uint32_t from_end = from_start + copy_size; 384 from_start < from_end; from_start++, to_start++) { 385 Object* hole_or_object = from->get(from_start); 386 if (hole_or_object == the_hole) { 387 to->set_the_hole(to_start); 388 } else { 389 to->set(to_start, hole_or_object->Number()); 390 } 391 } 392 } 393 394 395 static void CopyDictionaryToDoubleElements(FixedArrayBase* from_base, 396 uint32_t from_start, 397 FixedArrayBase* to_base, 398 uint32_t to_start, 399 int raw_copy_size) { 400 DisallowHeapAllocation no_allocation; 401 SeededNumberDictionary* from = SeededNumberDictionary::cast(from_base); 402 int copy_size = raw_copy_size; 403 if (copy_size < 0) { 404 DCHECK(copy_size == ElementsAccessor::kCopyToEnd || 405 copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 406 copy_size = from->max_number_key() + 1 - from_start; 407 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 408 for (int i = to_start + copy_size; i < to_base->length(); ++i) { 409 FixedDoubleArray::cast(to_base)->set_the_hole(i); 410 } 411 } 412 } 413 if (copy_size == 0) return; 414 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 415 uint32_t to_length = to->length(); 416 if (to_start + copy_size > to_length) { 417 copy_size = to_length - to_start; 418 } 419 for (int i = 0; i < copy_size; i++) { 420 int entry = from->FindEntry(i + from_start); 421 if (entry != SeededNumberDictionary::kNotFound) { 422 to->set(i + to_start, from->ValueAt(entry)->Number()); 423 } else { 424 to->set_the_hole(i + to_start); 425 } 426 } 427 } 428 429 static void TraceTopFrame(Isolate* isolate) { 430 StackFrameIterator it(isolate); 431 if (it.done()) { 432 PrintF("unknown location (no JavaScript frames present)"); 433 return; 434 } 435 StackFrame* raw_frame = it.frame(); 436 if (raw_frame->is_internal()) { 437 Code* apply_builtin = 438 isolate->builtins()->builtin(Builtins::kFunctionPrototypeApply); 439 if (raw_frame->unchecked_code() == apply_builtin) { 440 PrintF("apply from "); 441 it.Advance(); 442 raw_frame = it.frame(); 443 } 444 } 445 JavaScriptFrame::PrintTop(isolate, stdout, false, true); 446 } 447 448 static void SortIndices( 449 Handle<FixedArray> indices, uint32_t sort_size, 450 WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER) { 451 struct { 452 bool operator()(Object* a, Object* b) { 453 if (a->IsSmi() || !a->IsUndefined(HeapObject::cast(a)->GetIsolate())) { 454 if (!b->IsSmi() && b->IsUndefined(HeapObject::cast(b)->GetIsolate())) { 455 return true; 456 } 457 return a->Number() < b->Number(); 458 } 459 return !b->IsSmi() && b->IsUndefined(HeapObject::cast(b)->GetIsolate()); 460 } 461 } cmp; 462 Object** start = 463 reinterpret_cast<Object**>(indices->GetFirstElementAddress()); 464 std::sort(start, start + sort_size, cmp); 465 if (write_barrier_mode != SKIP_WRITE_BARRIER) { 466 FIXED_ARRAY_ELEMENTS_WRITE_BARRIER(indices->GetIsolate()->heap(), *indices, 467 0, sort_size); 468 } 469 } 470 471 static Maybe<bool> IncludesValueSlowPath(Isolate* isolate, 472 Handle<JSObject> receiver, 473 Handle<Object> value, 474 uint32_t start_from, uint32_t length) { 475 bool search_for_hole = value->IsUndefined(isolate); 476 for (uint32_t k = start_from; k < length; ++k) { 477 LookupIterator it(isolate, receiver, k); 478 if (!it.IsFound()) { 479 if (search_for_hole) return Just(true); 480 continue; 481 } 482 Handle<Object> element_k; 483 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k, 484 Object::GetProperty(&it), Nothing<bool>()); 485 486 if (value->SameValueZero(*element_k)) return Just(true); 487 } 488 489 return Just(false); 490 } 491 492 static Maybe<int64_t> IndexOfValueSlowPath(Isolate* isolate, 493 Handle<JSObject> receiver, 494 Handle<Object> value, 495 uint32_t start_from, 496 uint32_t length) { 497 for (uint32_t k = start_from; k < length; ++k) { 498 LookupIterator it(isolate, receiver, k); 499 if (!it.IsFound()) { 500 continue; 501 } 502 Handle<Object> element_k; 503 ASSIGN_RETURN_ON_EXCEPTION_VALUE( 504 isolate, element_k, Object::GetProperty(&it), Nothing<int64_t>()); 505 506 if (value->StrictEquals(*element_k)) return Just<int64_t>(k); 507 } 508 509 return Just<int64_t>(-1); 510 } 511 512 // Base class for element handler implementations. Contains the 513 // the common logic for objects with different ElementsKinds. 514 // Subclasses must specialize method for which the element 515 // implementation differs from the base class implementation. 516 // 517 // This class is intended to be used in the following way: 518 // 519 // class SomeElementsAccessor : 520 // public ElementsAccessorBase<SomeElementsAccessor, 521 // BackingStoreClass> { 522 // ... 523 // } 524 // 525 // This is an example of the Curiously Recurring Template Pattern (see 526 // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use 527 // CRTP to guarantee aggressive compile time optimizations (i.e. inlining and 528 // specialization of SomeElementsAccessor methods). 529 template <typename Subclass, typename ElementsTraitsParam> 530 class ElementsAccessorBase : public ElementsAccessor { 531 public: 532 explicit ElementsAccessorBase(const char* name) 533 : ElementsAccessor(name) { } 534 535 typedef ElementsTraitsParam ElementsTraits; 536 typedef typename ElementsTraitsParam::BackingStore BackingStore; 537 538 static ElementsKind kind() { return ElementsTraits::Kind; } 539 540 static void ValidateContents(Handle<JSObject> holder, int length) { 541 } 542 543 static void ValidateImpl(Handle<JSObject> holder) { 544 Handle<FixedArrayBase> fixed_array_base(holder->elements()); 545 if (!fixed_array_base->IsHeapObject()) return; 546 // Arrays that have been shifted in place can't be verified. 547 if (fixed_array_base->IsFiller()) return; 548 int length = 0; 549 if (holder->IsJSArray()) { 550 Object* length_obj = Handle<JSArray>::cast(holder)->length(); 551 if (length_obj->IsSmi()) { 552 length = Smi::cast(length_obj)->value(); 553 } 554 } else { 555 length = fixed_array_base->length(); 556 } 557 Subclass::ValidateContents(holder, length); 558 } 559 560 void Validate(Handle<JSObject> holder) final { 561 DisallowHeapAllocation no_gc; 562 Subclass::ValidateImpl(holder); 563 } 564 565 static bool IsPackedImpl(Handle<JSObject> holder, 566 Handle<FixedArrayBase> backing_store, uint32_t start, 567 uint32_t end) { 568 if (IsFastPackedElementsKind(kind())) return true; 569 Isolate* isolate = backing_store->GetIsolate(); 570 for (uint32_t i = start; i < end; i++) { 571 if (!Subclass::HasElementImpl(isolate, holder, i, backing_store, 572 ALL_PROPERTIES)) { 573 return false; 574 } 575 } 576 return true; 577 } 578 579 static void TryTransitionResultArrayToPacked(Handle<JSArray> array) { 580 if (!IsHoleyElementsKind(kind())) return; 581 int length = Smi::cast(array->length())->value(); 582 Handle<FixedArrayBase> backing_store(array->elements()); 583 if (!Subclass::IsPackedImpl(array, backing_store, 0, length)) { 584 return; 585 } 586 ElementsKind packed_kind = GetPackedElementsKind(kind()); 587 Handle<Map> new_map = 588 JSObject::GetElementsTransitionMap(array, packed_kind); 589 JSObject::MigrateToMap(array, new_map); 590 if (FLAG_trace_elements_transitions) { 591 JSObject::PrintElementsTransition(stdout, array, kind(), backing_store, 592 packed_kind, backing_store); 593 } 594 } 595 596 bool HasElement(Handle<JSObject> holder, uint32_t index, 597 Handle<FixedArrayBase> backing_store, 598 PropertyFilter filter) final { 599 return Subclass::HasElementImpl(holder->GetIsolate(), holder, index, 600 backing_store, filter); 601 } 602 603 static bool HasElementImpl(Isolate* isolate, Handle<JSObject> holder, 604 uint32_t index, 605 Handle<FixedArrayBase> backing_store, 606 PropertyFilter filter) { 607 return Subclass::GetEntryForIndexImpl(isolate, *holder, *backing_store, 608 index, filter) != kMaxUInt32; 609 } 610 611 bool HasAccessors(JSObject* holder) final { 612 return Subclass::HasAccessorsImpl(holder, holder->elements()); 613 } 614 615 static bool HasAccessorsImpl(JSObject* holder, 616 FixedArrayBase* backing_store) { 617 return false; 618 } 619 620 Handle<Object> Get(Handle<JSObject> holder, uint32_t entry) final { 621 return Subclass::GetImpl(holder, entry); 622 } 623 624 static Handle<Object> GetImpl(Handle<JSObject> holder, uint32_t entry) { 625 return Subclass::GetImpl(holder->elements(), entry); 626 } 627 628 static Handle<Object> GetImpl(FixedArrayBase* backing_store, uint32_t entry) { 629 Isolate* isolate = backing_store->GetIsolate(); 630 uint32_t index = GetIndexForEntryImpl(backing_store, entry); 631 return handle(BackingStore::cast(backing_store)->get(index), isolate); 632 } 633 634 void Set(Handle<JSObject> holder, uint32_t entry, Object* value) final { 635 Subclass::SetImpl(holder, entry, value); 636 } 637 638 void Reconfigure(Handle<JSObject> object, Handle<FixedArrayBase> store, 639 uint32_t entry, Handle<Object> value, 640 PropertyAttributes attributes) final { 641 Subclass::ReconfigureImpl(object, store, entry, value, attributes); 642 } 643 644 static void ReconfigureImpl(Handle<JSObject> object, 645 Handle<FixedArrayBase> store, uint32_t entry, 646 Handle<Object> value, 647 PropertyAttributes attributes) { 648 UNREACHABLE(); 649 } 650 651 void Add(Handle<JSObject> object, uint32_t index, Handle<Object> value, 652 PropertyAttributes attributes, uint32_t new_capacity) final { 653 Subclass::AddImpl(object, index, value, attributes, new_capacity); 654 } 655 656 static void AddImpl(Handle<JSObject> object, uint32_t index, 657 Handle<Object> value, PropertyAttributes attributes, 658 uint32_t new_capacity) { 659 UNREACHABLE(); 660 } 661 662 uint32_t Push(Handle<JSArray> receiver, Arguments* args, 663 uint32_t push_size) final { 664 return Subclass::PushImpl(receiver, args, push_size); 665 } 666 667 static uint32_t PushImpl(Handle<JSArray> receiver, Arguments* args, 668 uint32_t push_sized) { 669 UNREACHABLE(); 670 return 0; 671 } 672 673 uint32_t Unshift(Handle<JSArray> receiver, Arguments* args, 674 uint32_t unshift_size) final { 675 return Subclass::UnshiftImpl(receiver, args, unshift_size); 676 } 677 678 static uint32_t UnshiftImpl(Handle<JSArray> receiver, Arguments* args, 679 uint32_t unshift_size) { 680 UNREACHABLE(); 681 return 0; 682 } 683 684 Handle<JSArray> Slice(Handle<JSObject> receiver, uint32_t start, 685 uint32_t end) final { 686 return Subclass::SliceImpl(receiver, start, end); 687 } 688 689 static Handle<JSArray> SliceImpl(Handle<JSObject> receiver, 690 uint32_t start, uint32_t end) { 691 UNREACHABLE(); 692 return Handle<JSArray>(); 693 } 694 695 Handle<JSArray> Splice(Handle<JSArray> receiver, uint32_t start, 696 uint32_t delete_count, Arguments* args, 697 uint32_t add_count) final { 698 return Subclass::SpliceImpl(receiver, start, delete_count, args, add_count); 699 } 700 701 static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver, 702 uint32_t start, uint32_t delete_count, 703 Arguments* args, uint32_t add_count) { 704 UNREACHABLE(); 705 return Handle<JSArray>(); 706 } 707 708 Handle<Object> Pop(Handle<JSArray> receiver) final { 709 return Subclass::PopImpl(receiver); 710 } 711 712 static Handle<Object> PopImpl(Handle<JSArray> receiver) { 713 UNREACHABLE(); 714 return Handle<Object>(); 715 } 716 717 Handle<Object> Shift(Handle<JSArray> receiver) final { 718 return Subclass::ShiftImpl(receiver); 719 } 720 721 static Handle<Object> ShiftImpl(Handle<JSArray> receiver) { 722 UNREACHABLE(); 723 return Handle<Object>(); 724 } 725 726 void SetLength(Handle<JSArray> array, uint32_t length) final { 727 Subclass::SetLengthImpl(array->GetIsolate(), array, length, 728 handle(array->elements())); 729 } 730 731 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array, 732 uint32_t length, 733 Handle<FixedArrayBase> backing_store) { 734 DCHECK(!array->SetLengthWouldNormalize(length)); 735 DCHECK(IsFastElementsKind(array->GetElementsKind())); 736 uint32_t old_length = 0; 737 CHECK(array->length()->ToArrayIndex(&old_length)); 738 739 if (old_length < length) { 740 ElementsKind kind = array->GetElementsKind(); 741 if (!IsFastHoleyElementsKind(kind)) { 742 kind = GetHoleyElementsKind(kind); 743 JSObject::TransitionElementsKind(array, kind); 744 } 745 } 746 747 // Check whether the backing store should be shrunk. 748 uint32_t capacity = backing_store->length(); 749 old_length = Min(old_length, capacity); 750 if (length == 0) { 751 array->initialize_elements(); 752 } else if (length <= capacity) { 753 if (IsFastSmiOrObjectElementsKind(kind())) { 754 JSObject::EnsureWritableFastElements(array); 755 if (array->elements() != *backing_store) { 756 backing_store = handle(array->elements(), isolate); 757 } 758 } 759 if (2 * length <= capacity) { 760 // If more than half the elements won't be used, trim the array. 761 isolate->heap()->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>( 762 *backing_store, capacity - length); 763 } else { 764 // Otherwise, fill the unused tail with holes. 765 for (uint32_t i = length; i < old_length; i++) { 766 BackingStore::cast(*backing_store)->set_the_hole(i); 767 } 768 } 769 } else { 770 // Check whether the backing store should be expanded. 771 capacity = Max(length, JSObject::NewElementsCapacity(capacity)); 772 Subclass::GrowCapacityAndConvertImpl(array, capacity); 773 } 774 775 array->set_length(Smi::FromInt(length)); 776 JSObject::ValidateElements(array); 777 } 778 779 uint32_t NumberOfElements(JSObject* receiver) final { 780 return Subclass::NumberOfElementsImpl(receiver, receiver->elements()); 781 } 782 783 static uint32_t NumberOfElementsImpl(JSObject* receiver, 784 FixedArrayBase* backing_store) { 785 UNREACHABLE(); 786 } 787 788 static uint32_t GetMaxIndex(JSObject* receiver, FixedArrayBase* elements) { 789 if (receiver->IsJSArray()) { 790 DCHECK(JSArray::cast(receiver)->length()->IsSmi()); 791 return static_cast<uint32_t>( 792 Smi::cast(JSArray::cast(receiver)->length())->value()); 793 } 794 return Subclass::GetCapacityImpl(receiver, elements); 795 } 796 797 static uint32_t GetMaxNumberOfEntries(JSObject* receiver, 798 FixedArrayBase* elements) { 799 return Subclass::GetMaxIndex(receiver, elements); 800 } 801 802 static Handle<FixedArrayBase> ConvertElementsWithCapacity( 803 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 804 ElementsKind from_kind, uint32_t capacity) { 805 return ConvertElementsWithCapacity( 806 object, old_elements, from_kind, capacity, 0, 0, 807 ElementsAccessor::kCopyToEndAndInitializeToHole); 808 } 809 810 static Handle<FixedArrayBase> ConvertElementsWithCapacity( 811 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 812 ElementsKind from_kind, uint32_t capacity, int copy_size) { 813 return ConvertElementsWithCapacity(object, old_elements, from_kind, 814 capacity, 0, 0, copy_size); 815 } 816 817 static Handle<FixedArrayBase> ConvertElementsWithCapacity( 818 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 819 ElementsKind from_kind, uint32_t capacity, uint32_t src_index, 820 uint32_t dst_index, int copy_size) { 821 Isolate* isolate = object->GetIsolate(); 822 Handle<FixedArrayBase> new_elements; 823 if (IsFastDoubleElementsKind(kind())) { 824 new_elements = isolate->factory()->NewFixedDoubleArray(capacity); 825 } else { 826 new_elements = isolate->factory()->NewUninitializedFixedArray(capacity); 827 } 828 829 int packed_size = kPackedSizeNotKnown; 830 if (IsFastPackedElementsKind(from_kind) && object->IsJSArray()) { 831 packed_size = Smi::cast(JSArray::cast(*object)->length())->value(); 832 } 833 834 Subclass::CopyElementsImpl(*old_elements, src_index, *new_elements, 835 from_kind, dst_index, packed_size, copy_size); 836 837 return new_elements; 838 } 839 840 static void TransitionElementsKindImpl(Handle<JSObject> object, 841 Handle<Map> to_map) { 842 Handle<Map> from_map = handle(object->map()); 843 ElementsKind from_kind = from_map->elements_kind(); 844 ElementsKind to_kind = to_map->elements_kind(); 845 if (IsFastHoleyElementsKind(from_kind)) { 846 to_kind = GetHoleyElementsKind(to_kind); 847 } 848 if (from_kind != to_kind) { 849 // This method should never be called for any other case. 850 DCHECK(IsFastElementsKind(from_kind)); 851 DCHECK(IsFastElementsKind(to_kind)); 852 DCHECK_NE(TERMINAL_FAST_ELEMENTS_KIND, from_kind); 853 854 Handle<FixedArrayBase> from_elements(object->elements()); 855 if (object->elements() == object->GetHeap()->empty_fixed_array() || 856 IsFastDoubleElementsKind(from_kind) == 857 IsFastDoubleElementsKind(to_kind)) { 858 // No change is needed to the elements() buffer, the transition 859 // only requires a map change. 860 JSObject::MigrateToMap(object, to_map); 861 } else { 862 DCHECK((IsFastSmiElementsKind(from_kind) && 863 IsFastDoubleElementsKind(to_kind)) || 864 (IsFastDoubleElementsKind(from_kind) && 865 IsFastObjectElementsKind(to_kind))); 866 uint32_t capacity = static_cast<uint32_t>(object->elements()->length()); 867 Handle<FixedArrayBase> elements = ConvertElementsWithCapacity( 868 object, from_elements, from_kind, capacity); 869 JSObject::SetMapAndElements(object, to_map, elements); 870 } 871 if (FLAG_trace_elements_transitions) { 872 JSObject::PrintElementsTransition(stdout, object, from_kind, 873 from_elements, to_kind, 874 handle(object->elements())); 875 } 876 } 877 } 878 879 static void GrowCapacityAndConvertImpl(Handle<JSObject> object, 880 uint32_t capacity) { 881 ElementsKind from_kind = object->GetElementsKind(); 882 if (IsFastSmiOrObjectElementsKind(from_kind)) { 883 // Array optimizations rely on the prototype lookups of Array objects 884 // always returning undefined. If there is a store to the initial 885 // prototype object, make sure all of these optimizations are invalidated. 886 object->GetIsolate()->UpdateArrayProtectorOnSetLength(object); 887 } 888 Handle<FixedArrayBase> old_elements(object->elements()); 889 // This method should only be called if there's a reason to update the 890 // elements. 891 DCHECK(IsFastDoubleElementsKind(from_kind) != 892 IsFastDoubleElementsKind(kind()) || 893 IsDictionaryElementsKind(from_kind) || 894 static_cast<uint32_t>(old_elements->length()) < capacity); 895 Subclass::BasicGrowCapacityAndConvertImpl(object, old_elements, from_kind, 896 kind(), capacity); 897 } 898 899 static void BasicGrowCapacityAndConvertImpl( 900 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 901 ElementsKind from_kind, ElementsKind to_kind, uint32_t capacity) { 902 Handle<FixedArrayBase> elements = 903 ConvertElementsWithCapacity(object, old_elements, from_kind, capacity); 904 905 if (IsHoleyElementsKind(from_kind)) to_kind = GetHoleyElementsKind(to_kind); 906 Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, to_kind); 907 JSObject::SetMapAndElements(object, new_map, elements); 908 909 // Transition through the allocation site as well if present. 910 JSObject::UpdateAllocationSite(object, to_kind); 911 912 if (FLAG_trace_elements_transitions) { 913 JSObject::PrintElementsTransition(stdout, object, from_kind, old_elements, 914 to_kind, elements); 915 } 916 } 917 918 void TransitionElementsKind(Handle<JSObject> object, Handle<Map> map) final { 919 Subclass::TransitionElementsKindImpl(object, map); 920 } 921 922 void GrowCapacityAndConvert(Handle<JSObject> object, 923 uint32_t capacity) final { 924 Subclass::GrowCapacityAndConvertImpl(object, capacity); 925 } 926 927 bool GrowCapacity(Handle<JSObject> object, uint32_t index) final { 928 // This function is intended to be called from optimized code. We don't 929 // want to trigger lazy deopts there, so refuse to handle cases that would. 930 if (object->map()->is_prototype_map() || 931 object->WouldConvertToSlowElements(index)) { 932 return false; 933 } 934 Handle<FixedArrayBase> old_elements(object->elements()); 935 uint32_t new_capacity = JSObject::NewElementsCapacity(index + 1); 936 DCHECK(static_cast<uint32_t>(old_elements->length()) < new_capacity); 937 Handle<FixedArrayBase> elements = 938 ConvertElementsWithCapacity(object, old_elements, kind(), new_capacity); 939 940 DCHECK_EQ(object->GetElementsKind(), kind()); 941 // Transition through the allocation site as well if present. 942 if (JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kCheckOnly>( 943 object, kind())) { 944 return false; 945 } 946 947 object->set_elements(*elements); 948 return true; 949 } 950 951 void Delete(Handle<JSObject> obj, uint32_t entry) final { 952 Subclass::DeleteImpl(obj, entry); 953 } 954 955 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 956 FixedArrayBase* to, ElementsKind from_kind, 957 uint32_t to_start, int packed_size, 958 int copy_size) { 959 UNREACHABLE(); 960 } 961 962 void CopyElements(JSObject* from_holder, uint32_t from_start, 963 ElementsKind from_kind, Handle<FixedArrayBase> to, 964 uint32_t to_start, int copy_size) final { 965 int packed_size = kPackedSizeNotKnown; 966 bool is_packed = IsFastPackedElementsKind(from_kind) && 967 from_holder->IsJSArray(); 968 if (is_packed) { 969 packed_size = 970 Smi::cast(JSArray::cast(from_holder)->length())->value(); 971 if (copy_size >= 0 && packed_size > copy_size) { 972 packed_size = copy_size; 973 } 974 } 975 FixedArrayBase* from = from_holder->elements(); 976 // NOTE: the Subclass::CopyElementsImpl() methods 977 // violate the handlified function signature convention: 978 // raw pointer parameters in the function that allocates. This is done 979 // intentionally to avoid ArrayConcat() builtin performance degradation. 980 // 981 // Details: The idea is that allocations actually happen only in case of 982 // copying from object with fast double elements to object with object 983 // elements. In all the other cases there are no allocations performed and 984 // handle creation causes noticeable performance degradation of the builtin. 985 Subclass::CopyElementsImpl(from, from_start, *to, from_kind, to_start, 986 packed_size, copy_size); 987 } 988 989 void CopyElements(Handle<FixedArrayBase> source, ElementsKind source_kind, 990 Handle<FixedArrayBase> destination, int size) { 991 Subclass::CopyElementsImpl(*source, 0, *destination, source_kind, 0, 992 kPackedSizeNotKnown, size); 993 } 994 995 Handle<SeededNumberDictionary> Normalize(Handle<JSObject> object) final { 996 return Subclass::NormalizeImpl(object, handle(object->elements())); 997 } 998 999 static Handle<SeededNumberDictionary> NormalizeImpl( 1000 Handle<JSObject> object, Handle<FixedArrayBase> elements) { 1001 UNREACHABLE(); 1002 return Handle<SeededNumberDictionary>(); 1003 } 1004 1005 Maybe<bool> CollectValuesOrEntries(Isolate* isolate, Handle<JSObject> object, 1006 Handle<FixedArray> values_or_entries, 1007 bool get_entries, int* nof_items, 1008 PropertyFilter filter) { 1009 return Subclass::CollectValuesOrEntriesImpl( 1010 isolate, object, values_or_entries, get_entries, nof_items, filter); 1011 } 1012 1013 static Maybe<bool> CollectValuesOrEntriesImpl( 1014 Isolate* isolate, Handle<JSObject> object, 1015 Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items, 1016 PropertyFilter filter) { 1017 int count = 0; 1018 KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly, 1019 ALL_PROPERTIES); 1020 Subclass::CollectElementIndicesImpl( 1021 object, handle(object->elements(), isolate), &accumulator); 1022 Handle<FixedArray> keys = accumulator.GetKeys(); 1023 1024 for (int i = 0; i < keys->length(); ++i) { 1025 Handle<Object> key(keys->get(i), isolate); 1026 Handle<Object> value; 1027 uint32_t index; 1028 if (!key->ToUint32(&index)) continue; 1029 1030 uint32_t entry = Subclass::GetEntryForIndexImpl( 1031 isolate, *object, object->elements(), index, filter); 1032 if (entry == kMaxUInt32) continue; 1033 1034 PropertyDetails details = Subclass::GetDetailsImpl(*object, entry); 1035 1036 if (details.kind() == kData) { 1037 value = Subclass::GetImpl(object, entry); 1038 } else { 1039 LookupIterator it(isolate, object, index, LookupIterator::OWN); 1040 ASSIGN_RETURN_ON_EXCEPTION_VALUE( 1041 isolate, value, Object::GetProperty(&it), Nothing<bool>()); 1042 } 1043 if (get_entries) { 1044 value = MakeEntryPair(isolate, index, value); 1045 } 1046 values_or_entries->set(count++, *value); 1047 } 1048 1049 *nof_items = count; 1050 return Just(true); 1051 } 1052 1053 void CollectElementIndices(Handle<JSObject> object, 1054 Handle<FixedArrayBase> backing_store, 1055 KeyAccumulator* keys) final { 1056 if (keys->filter() & ONLY_ALL_CAN_READ) return; 1057 Subclass::CollectElementIndicesImpl(object, backing_store, keys); 1058 } 1059 1060 static void CollectElementIndicesImpl(Handle<JSObject> object, 1061 Handle<FixedArrayBase> backing_store, 1062 KeyAccumulator* keys) { 1063 DCHECK_NE(DICTIONARY_ELEMENTS, kind()); 1064 // Non-dictionary elements can't have all-can-read accessors. 1065 uint32_t length = Subclass::GetMaxIndex(*object, *backing_store); 1066 PropertyFilter filter = keys->filter(); 1067 Isolate* isolate = keys->isolate(); 1068 Factory* factory = isolate->factory(); 1069 for (uint32_t i = 0; i < length; i++) { 1070 if (Subclass::HasElementImpl(isolate, object, i, backing_store, filter)) { 1071 keys->AddKey(factory->NewNumberFromUint(i)); 1072 } 1073 } 1074 } 1075 1076 static Handle<FixedArray> DirectCollectElementIndicesImpl( 1077 Isolate* isolate, Handle<JSObject> object, 1078 Handle<FixedArrayBase> backing_store, GetKeysConversion convert, 1079 PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices, 1080 uint32_t insertion_index = 0) { 1081 uint32_t length = Subclass::GetMaxIndex(*object, *backing_store); 1082 for (uint32_t i = 0; i < length; i++) { 1083 if (Subclass::HasElementImpl(isolate, object, i, backing_store, filter)) { 1084 if (convert == GetKeysConversion::kConvertToString) { 1085 Handle<String> index_string = isolate->factory()->Uint32ToString(i); 1086 list->set(insertion_index, *index_string); 1087 } else { 1088 list->set(insertion_index, Smi::FromInt(i), SKIP_WRITE_BARRIER); 1089 } 1090 insertion_index++; 1091 } 1092 } 1093 *nof_indices = insertion_index; 1094 return list; 1095 } 1096 1097 MaybeHandle<FixedArray> PrependElementIndices( 1098 Handle<JSObject> object, Handle<FixedArrayBase> backing_store, 1099 Handle<FixedArray> keys, GetKeysConversion convert, 1100 PropertyFilter filter) final { 1101 return Subclass::PrependElementIndicesImpl(object, backing_store, keys, 1102 convert, filter); 1103 } 1104 1105 static MaybeHandle<FixedArray> PrependElementIndicesImpl( 1106 Handle<JSObject> object, Handle<FixedArrayBase> backing_store, 1107 Handle<FixedArray> keys, GetKeysConversion convert, 1108 PropertyFilter filter) { 1109 Isolate* isolate = object->GetIsolate(); 1110 uint32_t nof_property_keys = keys->length(); 1111 uint32_t initial_list_length = 1112 Subclass::GetMaxNumberOfEntries(*object, *backing_store); 1113 1114 initial_list_length += nof_property_keys; 1115 if (initial_list_length > FixedArray::kMaxLength || 1116 initial_list_length < nof_property_keys) { 1117 return isolate->Throw<FixedArray>(isolate->factory()->NewRangeError( 1118 MessageTemplate::kInvalidArrayLength)); 1119 } 1120 1121 // Collect the element indices into a new list. 1122 MaybeHandle<FixedArray> raw_array = 1123 isolate->factory()->TryNewFixedArray(initial_list_length); 1124 Handle<FixedArray> combined_keys; 1125 1126 // If we have a holey backing store try to precisely estimate the backing 1127 // store size as a last emergency measure if we cannot allocate the big 1128 // array. 1129 if (!raw_array.ToHandle(&combined_keys)) { 1130 if (IsHoleyElementsKind(kind())) { 1131 // If we overestimate the result list size we might end up in the 1132 // large-object space which doesn't free memory on shrinking the list. 1133 // Hence we try to estimate the final size for holey backing stores more 1134 // precisely here. 1135 initial_list_length = 1136 Subclass::NumberOfElementsImpl(*object, *backing_store); 1137 initial_list_length += nof_property_keys; 1138 } 1139 combined_keys = isolate->factory()->NewFixedArray(initial_list_length); 1140 } 1141 1142 uint32_t nof_indices = 0; 1143 bool needs_sorting = 1144 IsDictionaryElementsKind(kind()) || IsSloppyArgumentsElements(kind()); 1145 combined_keys = Subclass::DirectCollectElementIndicesImpl( 1146 isolate, object, backing_store, 1147 needs_sorting ? GetKeysConversion::kKeepNumbers : convert, filter, 1148 combined_keys, &nof_indices); 1149 1150 if (needs_sorting) { 1151 SortIndices(combined_keys, nof_indices); 1152 // Indices from dictionary elements should only be converted after 1153 // sorting. 1154 if (convert == GetKeysConversion::kConvertToString) { 1155 for (uint32_t i = 0; i < nof_indices; i++) { 1156 Handle<Object> index_string = isolate->factory()->Uint32ToString( 1157 combined_keys->get(i)->Number()); 1158 combined_keys->set(i, *index_string); 1159 } 1160 } 1161 } 1162 1163 // Copy over the passed-in property keys. 1164 CopyObjectToObjectElements(*keys, FAST_ELEMENTS, 0, *combined_keys, 1165 FAST_ELEMENTS, nof_indices, nof_property_keys); 1166 1167 // For holey elements and arguments we might have to shrink the collected 1168 // keys since the estimates might be off. 1169 if (IsHoleyElementsKind(kind()) || IsSloppyArgumentsElements(kind())) { 1170 // Shrink combined_keys to the final size. 1171 int final_size = nof_indices + nof_property_keys; 1172 DCHECK_LE(final_size, combined_keys->length()); 1173 combined_keys->Shrink(final_size); 1174 } 1175 1176 return combined_keys; 1177 } 1178 1179 void AddElementsToKeyAccumulator(Handle<JSObject> receiver, 1180 KeyAccumulator* accumulator, 1181 AddKeyConversion convert) final { 1182 Subclass::AddElementsToKeyAccumulatorImpl(receiver, accumulator, convert); 1183 } 1184 1185 static uint32_t GetCapacityImpl(JSObject* holder, 1186 FixedArrayBase* backing_store) { 1187 return backing_store->length(); 1188 } 1189 1190 uint32_t GetCapacity(JSObject* holder, FixedArrayBase* backing_store) final { 1191 return Subclass::GetCapacityImpl(holder, backing_store); 1192 } 1193 1194 static Maybe<bool> IncludesValueImpl(Isolate* isolate, 1195 Handle<JSObject> receiver, 1196 Handle<Object> value, 1197 uint32_t start_from, uint32_t length) { 1198 return IncludesValueSlowPath(isolate, receiver, value, start_from, length); 1199 } 1200 1201 Maybe<bool> IncludesValue(Isolate* isolate, Handle<JSObject> receiver, 1202 Handle<Object> value, uint32_t start_from, 1203 uint32_t length) final { 1204 return Subclass::IncludesValueImpl(isolate, receiver, value, start_from, 1205 length); 1206 } 1207 1208 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate, 1209 Handle<JSObject> receiver, 1210 Handle<Object> value, 1211 uint32_t start_from, uint32_t length) { 1212 return IndexOfValueSlowPath(isolate, receiver, value, start_from, length); 1213 } 1214 1215 Maybe<int64_t> IndexOfValue(Isolate* isolate, Handle<JSObject> receiver, 1216 Handle<Object> value, uint32_t start_from, 1217 uint32_t length) final { 1218 return Subclass::IndexOfValueImpl(isolate, receiver, value, start_from, 1219 length); 1220 } 1221 1222 static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store, 1223 uint32_t entry) { 1224 return entry; 1225 } 1226 1227 static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder, 1228 FixedArrayBase* backing_store, 1229 uint32_t index, PropertyFilter filter) { 1230 uint32_t length = Subclass::GetMaxIndex(holder, backing_store); 1231 if (IsHoleyElementsKind(kind())) { 1232 return index < length && 1233 !BackingStore::cast(backing_store) 1234 ->is_the_hole(isolate, index) 1235 ? index 1236 : kMaxUInt32; 1237 } else { 1238 return index < length ? index : kMaxUInt32; 1239 } 1240 } 1241 1242 uint32_t GetEntryForIndex(Isolate* isolate, JSObject* holder, 1243 FixedArrayBase* backing_store, 1244 uint32_t index) final { 1245 return Subclass::GetEntryForIndexImpl(isolate, holder, backing_store, index, 1246 ALL_PROPERTIES); 1247 } 1248 1249 static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, 1250 uint32_t entry) { 1251 return PropertyDetails(NONE, DATA, 0, PropertyCellType::kNoCell); 1252 } 1253 1254 static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) { 1255 return PropertyDetails(NONE, DATA, 0, PropertyCellType::kNoCell); 1256 } 1257 1258 PropertyDetails GetDetails(JSObject* holder, uint32_t entry) final { 1259 return Subclass::GetDetailsImpl(holder, entry); 1260 } 1261 1262 private: 1263 DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase); 1264 }; 1265 1266 1267 class DictionaryElementsAccessor 1268 : public ElementsAccessorBase<DictionaryElementsAccessor, 1269 ElementsKindTraits<DICTIONARY_ELEMENTS> > { 1270 public: 1271 explicit DictionaryElementsAccessor(const char* name) 1272 : ElementsAccessorBase<DictionaryElementsAccessor, 1273 ElementsKindTraits<DICTIONARY_ELEMENTS> >(name) {} 1274 1275 static uint32_t GetMaxIndex(JSObject* receiver, FixedArrayBase* elements) { 1276 // We cannot properly estimate this for dictionaries. 1277 UNREACHABLE(); 1278 } 1279 1280 static uint32_t GetMaxNumberOfEntries(JSObject* receiver, 1281 FixedArrayBase* backing_store) { 1282 return NumberOfElementsImpl(receiver, backing_store); 1283 } 1284 1285 static uint32_t NumberOfElementsImpl(JSObject* receiver, 1286 FixedArrayBase* backing_store) { 1287 SeededNumberDictionary* dict = SeededNumberDictionary::cast(backing_store); 1288 return dict->NumberOfElements(); 1289 } 1290 1291 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array, 1292 uint32_t length, 1293 Handle<FixedArrayBase> backing_store) { 1294 Handle<SeededNumberDictionary> dict = 1295 Handle<SeededNumberDictionary>::cast(backing_store); 1296 int capacity = dict->Capacity(); 1297 uint32_t old_length = 0; 1298 CHECK(array->length()->ToArrayLength(&old_length)); 1299 if (length < old_length) { 1300 if (dict->requires_slow_elements()) { 1301 // Find last non-deletable element in range of elements to be 1302 // deleted and adjust range accordingly. 1303 for (int entry = 0; entry < capacity; entry++) { 1304 DisallowHeapAllocation no_gc; 1305 Object* index = dict->KeyAt(entry); 1306 if (index->IsNumber()) { 1307 uint32_t number = static_cast<uint32_t>(index->Number()); 1308 if (length <= number && number < old_length) { 1309 PropertyDetails details = dict->DetailsAt(entry); 1310 if (!details.IsConfigurable()) length = number + 1; 1311 } 1312 } 1313 } 1314 } 1315 1316 if (length == 0) { 1317 // Flush the backing store. 1318 JSObject::ResetElements(array); 1319 } else { 1320 DisallowHeapAllocation no_gc; 1321 // Remove elements that should be deleted. 1322 int removed_entries = 0; 1323 Handle<Object> the_hole_value = isolate->factory()->the_hole_value(); 1324 for (int entry = 0; entry < capacity; entry++) { 1325 Object* index = dict->KeyAt(entry); 1326 if (index->IsNumber()) { 1327 uint32_t number = static_cast<uint32_t>(index->Number()); 1328 if (length <= number && number < old_length) { 1329 dict->SetEntry(entry, the_hole_value, the_hole_value); 1330 removed_entries++; 1331 } 1332 } 1333 } 1334 1335 // Update the number of elements. 1336 dict->ElementsRemoved(removed_entries); 1337 } 1338 } 1339 1340 Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length); 1341 array->set_length(*length_obj); 1342 } 1343 1344 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 1345 FixedArrayBase* to, ElementsKind from_kind, 1346 uint32_t to_start, int packed_size, 1347 int copy_size) { 1348 UNREACHABLE(); 1349 } 1350 1351 1352 static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) { 1353 // TODO(verwaest): Remove reliance on index in Shrink. 1354 Handle<SeededNumberDictionary> dict( 1355 SeededNumberDictionary::cast(obj->elements())); 1356 uint32_t index = GetIndexForEntryImpl(*dict, entry); 1357 Handle<Object> result = SeededNumberDictionary::DeleteProperty(dict, entry); 1358 USE(result); 1359 DCHECK(result->IsTrue(dict->GetIsolate())); 1360 Handle<FixedArray> new_elements = 1361 SeededNumberDictionary::Shrink(dict, index); 1362 obj->set_elements(*new_elements); 1363 } 1364 1365 static bool HasAccessorsImpl(JSObject* holder, 1366 FixedArrayBase* backing_store) { 1367 DisallowHeapAllocation no_gc; 1368 SeededNumberDictionary* dict = SeededNumberDictionary::cast(backing_store); 1369 if (!dict->requires_slow_elements()) return false; 1370 int capacity = dict->Capacity(); 1371 Isolate* isolate = dict->GetIsolate(); 1372 for (int i = 0; i < capacity; i++) { 1373 Object* key = dict->KeyAt(i); 1374 if (!dict->IsKey(isolate, key)) continue; 1375 DCHECK(!dict->IsDeleted(i)); 1376 PropertyDetails details = dict->DetailsAt(i); 1377 if (details.type() == ACCESSOR_CONSTANT) return true; 1378 } 1379 return false; 1380 } 1381 1382 static Object* GetRaw(FixedArrayBase* store, uint32_t entry) { 1383 SeededNumberDictionary* backing_store = SeededNumberDictionary::cast(store); 1384 return backing_store->ValueAt(entry); 1385 } 1386 1387 static Handle<Object> GetImpl(Handle<JSObject> holder, uint32_t entry) { 1388 return GetImpl(holder->elements(), entry); 1389 } 1390 1391 static Handle<Object> GetImpl(FixedArrayBase* backing_store, uint32_t entry) { 1392 return handle(GetRaw(backing_store, entry), backing_store->GetIsolate()); 1393 } 1394 1395 static inline void SetImpl(Handle<JSObject> holder, uint32_t entry, 1396 Object* value) { 1397 SetImpl(holder->elements(), entry, value); 1398 } 1399 1400 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 1401 Object* value) { 1402 SeededNumberDictionary::cast(backing_store)->ValueAtPut(entry, value); 1403 } 1404 1405 static void ReconfigureImpl(Handle<JSObject> object, 1406 Handle<FixedArrayBase> store, uint32_t entry, 1407 Handle<Object> value, 1408 PropertyAttributes attributes) { 1409 SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(*store); 1410 if (attributes != NONE) object->RequireSlowElements(dictionary); 1411 dictionary->ValueAtPut(entry, *value); 1412 PropertyDetails details = dictionary->DetailsAt(entry); 1413 details = PropertyDetails(attributes, DATA, details.dictionary_index(), 1414 PropertyCellType::kNoCell); 1415 dictionary->DetailsAtPut(entry, details); 1416 } 1417 1418 static void AddImpl(Handle<JSObject> object, uint32_t index, 1419 Handle<Object> value, PropertyAttributes attributes, 1420 uint32_t new_capacity) { 1421 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); 1422 Handle<SeededNumberDictionary> dictionary = 1423 object->HasFastElements() || object->HasFastStringWrapperElements() 1424 ? JSObject::NormalizeElements(object) 1425 : handle(SeededNumberDictionary::cast(object->elements())); 1426 Handle<SeededNumberDictionary> new_dictionary = 1427 SeededNumberDictionary::AddNumberEntry( 1428 dictionary, index, value, details, 1429 object->map()->is_prototype_map()); 1430 if (attributes != NONE) object->RequireSlowElements(*new_dictionary); 1431 if (dictionary.is_identical_to(new_dictionary)) return; 1432 object->set_elements(*new_dictionary); 1433 } 1434 1435 static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* store, 1436 uint32_t entry) { 1437 DisallowHeapAllocation no_gc; 1438 SeededNumberDictionary* dict = SeededNumberDictionary::cast(store); 1439 Object* index = dict->KeyAt(entry); 1440 return !index->IsTheHole(isolate); 1441 } 1442 1443 static uint32_t GetIndexForEntryImpl(FixedArrayBase* store, uint32_t entry) { 1444 DisallowHeapAllocation no_gc; 1445 SeededNumberDictionary* dict = SeededNumberDictionary::cast(store); 1446 uint32_t result = 0; 1447 CHECK(dict->KeyAt(entry)->ToArrayIndex(&result)); 1448 return result; 1449 } 1450 1451 static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder, 1452 FixedArrayBase* store, uint32_t index, 1453 PropertyFilter filter) { 1454 DisallowHeapAllocation no_gc; 1455 SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(store); 1456 int entry = dictionary->FindEntry(isolate, index); 1457 if (entry == SeededNumberDictionary::kNotFound) return kMaxUInt32; 1458 if (filter != ALL_PROPERTIES) { 1459 PropertyDetails details = dictionary->DetailsAt(entry); 1460 PropertyAttributes attr = details.attributes(); 1461 if ((attr & filter) != 0) return kMaxUInt32; 1462 } 1463 return static_cast<uint32_t>(entry); 1464 } 1465 1466 static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) { 1467 return GetDetailsImpl(holder->elements(), entry); 1468 } 1469 1470 static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, 1471 uint32_t entry) { 1472 return SeededNumberDictionary::cast(backing_store)->DetailsAt(entry); 1473 } 1474 1475 static uint32_t FilterKey(Handle<SeededNumberDictionary> dictionary, 1476 int entry, Object* raw_key, PropertyFilter filter) { 1477 DCHECK(!dictionary->IsDeleted(entry)); 1478 DCHECK(raw_key->IsNumber()); 1479 DCHECK_LE(raw_key->Number(), kMaxUInt32); 1480 PropertyDetails details = dictionary->DetailsAt(entry); 1481 PropertyAttributes attr = details.attributes(); 1482 if ((attr & filter) != 0) return kMaxUInt32; 1483 return static_cast<uint32_t>(raw_key->Number()); 1484 } 1485 1486 static uint32_t GetKeyForEntryImpl(Isolate* isolate, 1487 Handle<SeededNumberDictionary> dictionary, 1488 int entry, PropertyFilter filter) { 1489 DisallowHeapAllocation no_gc; 1490 Object* raw_key = dictionary->KeyAt(entry); 1491 if (!dictionary->IsKey(isolate, raw_key)) return kMaxUInt32; 1492 return FilterKey(dictionary, entry, raw_key, filter); 1493 } 1494 1495 static void CollectElementIndicesImpl(Handle<JSObject> object, 1496 Handle<FixedArrayBase> backing_store, 1497 KeyAccumulator* keys) { 1498 if (keys->filter() & SKIP_STRINGS) return; 1499 Isolate* isolate = keys->isolate(); 1500 Handle<SeededNumberDictionary> dictionary = 1501 Handle<SeededNumberDictionary>::cast(backing_store); 1502 int capacity = dictionary->Capacity(); 1503 Handle<FixedArray> elements = isolate->factory()->NewFixedArray( 1504 GetMaxNumberOfEntries(*object, *backing_store)); 1505 int insertion_index = 0; 1506 PropertyFilter filter = keys->filter(); 1507 for (int i = 0; i < capacity; i++) { 1508 Object* raw_key = dictionary->KeyAt(i); 1509 if (!dictionary->IsKey(isolate, raw_key)) continue; 1510 uint32_t key = FilterKey(dictionary, i, raw_key, filter); 1511 if (key == kMaxUInt32) { 1512 keys->AddShadowingKey(raw_key); 1513 continue; 1514 } 1515 elements->set(insertion_index, raw_key); 1516 insertion_index++; 1517 } 1518 SortIndices(elements, insertion_index); 1519 for (int i = 0; i < insertion_index; i++) { 1520 keys->AddKey(elements->get(i)); 1521 } 1522 } 1523 1524 static Handle<FixedArray> DirectCollectElementIndicesImpl( 1525 Isolate* isolate, Handle<JSObject> object, 1526 Handle<FixedArrayBase> backing_store, GetKeysConversion convert, 1527 PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices, 1528 uint32_t insertion_index = 0) { 1529 if (filter & SKIP_STRINGS) return list; 1530 if (filter & ONLY_ALL_CAN_READ) return list; 1531 1532 Handle<SeededNumberDictionary> dictionary = 1533 Handle<SeededNumberDictionary>::cast(backing_store); 1534 uint32_t capacity = dictionary->Capacity(); 1535 for (uint32_t i = 0; i < capacity; i++) { 1536 uint32_t key = GetKeyForEntryImpl(isolate, dictionary, i, filter); 1537 if (key == kMaxUInt32) continue; 1538 Handle<Object> index = isolate->factory()->NewNumberFromUint(key); 1539 list->set(insertion_index, *index); 1540 insertion_index++; 1541 } 1542 *nof_indices = insertion_index; 1543 return list; 1544 } 1545 1546 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver, 1547 KeyAccumulator* accumulator, 1548 AddKeyConversion convert) { 1549 Isolate* isolate = accumulator->isolate(); 1550 Handle<Object> undefined = isolate->factory()->undefined_value(); 1551 Handle<Object> the_hole = isolate->factory()->the_hole_value(); 1552 Handle<SeededNumberDictionary> dictionary( 1553 SeededNumberDictionary::cast(receiver->elements()), isolate); 1554 int capacity = dictionary->Capacity(); 1555 for (int i = 0; i < capacity; i++) { 1556 Object* k = dictionary->KeyAt(i); 1557 if (k == *undefined) continue; 1558 if (k == *the_hole) continue; 1559 if (dictionary->IsDeleted(i)) continue; 1560 Object* value = dictionary->ValueAt(i); 1561 DCHECK(!value->IsTheHole(isolate)); 1562 DCHECK(!value->IsAccessorPair()); 1563 DCHECK(!value->IsAccessorInfo()); 1564 accumulator->AddKey(value, convert); 1565 } 1566 } 1567 1568 static bool IncludesValueFastPath(Isolate* isolate, Handle<JSObject> receiver, 1569 Handle<Object> value, uint32_t start_from, 1570 uint32_t length, Maybe<bool>* result) { 1571 DisallowHeapAllocation no_gc; 1572 SeededNumberDictionary* dictionary = 1573 SeededNumberDictionary::cast(receiver->elements()); 1574 int capacity = dictionary->Capacity(); 1575 Object* the_hole = isolate->heap()->the_hole_value(); 1576 Object* undefined = isolate->heap()->undefined_value(); 1577 1578 // Scan for accessor properties. If accessors are present, then elements 1579 // must be accessed in order via the slow path. 1580 bool found = false; 1581 for (int i = 0; i < capacity; ++i) { 1582 Object* k = dictionary->KeyAt(i); 1583 if (k == the_hole) continue; 1584 if (k == undefined) continue; 1585 1586 uint32_t index; 1587 if (!k->ToArrayIndex(&index) || index < start_from || index >= length) { 1588 continue; 1589 } 1590 1591 if (dictionary->DetailsAt(i).type() == ACCESSOR_CONSTANT) { 1592 // Restart from beginning in slow path, otherwise we may observably 1593 // access getters out of order 1594 return false; 1595 } else if (!found) { 1596 Object* element_k = dictionary->ValueAt(i); 1597 if (value->SameValueZero(element_k)) found = true; 1598 } 1599 } 1600 1601 *result = Just(found); 1602 return true; 1603 } 1604 1605 static Maybe<bool> IncludesValueImpl(Isolate* isolate, 1606 Handle<JSObject> receiver, 1607 Handle<Object> value, 1608 uint32_t start_from, uint32_t length) { 1609 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver)); 1610 bool search_for_hole = value->IsUndefined(isolate); 1611 1612 if (!search_for_hole) { 1613 Maybe<bool> result = Nothing<bool>(); 1614 if (DictionaryElementsAccessor::IncludesValueFastPath( 1615 isolate, receiver, value, start_from, length, &result)) { 1616 return result; 1617 } 1618 } 1619 1620 Handle<SeededNumberDictionary> dictionary( 1621 SeededNumberDictionary::cast(receiver->elements()), isolate); 1622 // Iterate through entire range, as accessing elements out of order is 1623 // observable 1624 for (uint32_t k = start_from; k < length; ++k) { 1625 int entry = dictionary->FindEntry(k); 1626 if (entry == SeededNumberDictionary::kNotFound) { 1627 if (search_for_hole) return Just(true); 1628 continue; 1629 } 1630 1631 PropertyDetails details = GetDetailsImpl(*dictionary, entry); 1632 switch (details.kind()) { 1633 case kData: { 1634 Object* element_k = dictionary->ValueAt(entry); 1635 if (value->SameValueZero(element_k)) return Just(true); 1636 break; 1637 } 1638 case kAccessor: { 1639 LookupIterator it(isolate, receiver, k, 1640 LookupIterator::OWN_SKIP_INTERCEPTOR); 1641 DCHECK(it.IsFound()); 1642 DCHECK_EQ(it.state(), LookupIterator::ACCESSOR); 1643 Handle<Object> element_k; 1644 1645 ASSIGN_RETURN_ON_EXCEPTION_VALUE( 1646 isolate, element_k, JSObject::GetPropertyWithAccessor(&it), 1647 Nothing<bool>()); 1648 1649 if (value->SameValueZero(*element_k)) return Just(true); 1650 1651 // Bailout to slow path if elements on prototype changed 1652 if (!JSObject::PrototypeHasNoElements(isolate, *receiver)) { 1653 return IncludesValueSlowPath(isolate, receiver, value, k + 1, 1654 length); 1655 } 1656 1657 // Continue if elements unchanged 1658 if (*dictionary == receiver->elements()) continue; 1659 1660 // Otherwise, bailout or update elements 1661 if (receiver->GetElementsKind() != DICTIONARY_ELEMENTS) { 1662 if (receiver->map()->GetInitialElements() == receiver->elements()) { 1663 // If switched to initial elements, return true if searching for 1664 // undefined, and false otherwise. 1665 return Just(search_for_hole); 1666 } 1667 // Otherwise, switch to slow path. 1668 return IncludesValueSlowPath(isolate, receiver, value, k + 1, 1669 length); 1670 } 1671 dictionary = handle( 1672 SeededNumberDictionary::cast(receiver->elements()), isolate); 1673 break; 1674 } 1675 } 1676 } 1677 return Just(false); 1678 } 1679 1680 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate, 1681 Handle<JSObject> receiver, 1682 Handle<Object> value, 1683 uint32_t start_from, uint32_t length) { 1684 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver)); 1685 1686 Handle<SeededNumberDictionary> dictionary( 1687 SeededNumberDictionary::cast(receiver->elements()), isolate); 1688 // Iterate through entire range, as accessing elements out of order is 1689 // observable. 1690 for (uint32_t k = start_from; k < length; ++k) { 1691 int entry = dictionary->FindEntry(k); 1692 if (entry == SeededNumberDictionary::kNotFound) { 1693 continue; 1694 } 1695 1696 PropertyDetails details = GetDetailsImpl(*dictionary, entry); 1697 switch (details.kind()) { 1698 case kData: { 1699 Object* element_k = dictionary->ValueAt(entry); 1700 if (value->StrictEquals(element_k)) { 1701 return Just<int64_t>(k); 1702 } 1703 break; 1704 } 1705 case kAccessor: { 1706 LookupIterator it(isolate, receiver, k, 1707 LookupIterator::OWN_SKIP_INTERCEPTOR); 1708 DCHECK(it.IsFound()); 1709 DCHECK_EQ(it.state(), LookupIterator::ACCESSOR); 1710 Handle<Object> element_k; 1711 1712 ASSIGN_RETURN_ON_EXCEPTION_VALUE( 1713 isolate, element_k, JSObject::GetPropertyWithAccessor(&it), 1714 Nothing<int64_t>()); 1715 1716 if (value->StrictEquals(*element_k)) return Just<int64_t>(k); 1717 1718 // Bailout to slow path if elements on prototype changed. 1719 if (!JSObject::PrototypeHasNoElements(isolate, *receiver)) { 1720 return IndexOfValueSlowPath(isolate, receiver, value, k + 1, 1721 length); 1722 } 1723 1724 // Continue if elements unchanged. 1725 if (*dictionary == receiver->elements()) continue; 1726 1727 // Otherwise, bailout or update elements. 1728 if (receiver->GetElementsKind() != DICTIONARY_ELEMENTS) { 1729 // Otherwise, switch to slow path. 1730 return IndexOfValueSlowPath(isolate, receiver, value, k + 1, 1731 length); 1732 } 1733 dictionary = handle( 1734 SeededNumberDictionary::cast(receiver->elements()), isolate); 1735 break; 1736 } 1737 } 1738 } 1739 return Just<int64_t>(-1); 1740 } 1741 }; 1742 1743 1744 // Super class for all fast element arrays. 1745 template <typename Subclass, typename KindTraits> 1746 class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> { 1747 public: 1748 explicit FastElementsAccessor(const char* name) 1749 : ElementsAccessorBase<Subclass, KindTraits>(name) {} 1750 1751 typedef typename KindTraits::BackingStore BackingStore; 1752 1753 static Handle<SeededNumberDictionary> NormalizeImpl( 1754 Handle<JSObject> object, Handle<FixedArrayBase> store) { 1755 Isolate* isolate = store->GetIsolate(); 1756 ElementsKind kind = Subclass::kind(); 1757 1758 // Ensure that notifications fire if the array or object prototypes are 1759 // normalizing. 1760 if (IsFastSmiOrObjectElementsKind(kind)) { 1761 isolate->UpdateArrayProtectorOnNormalizeElements(object); 1762 } 1763 1764 int capacity = object->GetFastElementsUsage(); 1765 Handle<SeededNumberDictionary> dictionary = 1766 SeededNumberDictionary::New(isolate, capacity); 1767 1768 PropertyDetails details = PropertyDetails::Empty(); 1769 bool used_as_prototype = object->map()->is_prototype_map(); 1770 int j = 0; 1771 for (int i = 0; j < capacity; i++) { 1772 if (IsHoleyElementsKind(kind)) { 1773 if (BackingStore::cast(*store)->is_the_hole(isolate, i)) continue; 1774 } 1775 Handle<Object> value = Subclass::GetImpl(*store, i); 1776 dictionary = SeededNumberDictionary::AddNumberEntry( 1777 dictionary, i, value, details, used_as_prototype); 1778 j++; 1779 } 1780 return dictionary; 1781 } 1782 1783 static void DeleteAtEnd(Handle<JSObject> obj, 1784 Handle<BackingStore> backing_store, uint32_t entry) { 1785 uint32_t length = static_cast<uint32_t>(backing_store->length()); 1786 Isolate* isolate = obj->GetIsolate(); 1787 for (; entry > 0; entry--) { 1788 if (!backing_store->is_the_hole(isolate, entry - 1)) break; 1789 } 1790 if (entry == 0) { 1791 FixedArray* empty = isolate->heap()->empty_fixed_array(); 1792 // Dynamically ask for the elements kind here since we manually redirect 1793 // the operations for argument backing stores. 1794 if (obj->GetElementsKind() == FAST_SLOPPY_ARGUMENTS_ELEMENTS) { 1795 FixedArray::cast(obj->elements())->set(1, empty); 1796 } else { 1797 obj->set_elements(empty); 1798 } 1799 return; 1800 } 1801 1802 isolate->heap()->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>( 1803 *backing_store, length - entry); 1804 } 1805 1806 static void DeleteCommon(Handle<JSObject> obj, uint32_t entry, 1807 Handle<FixedArrayBase> store) { 1808 DCHECK(obj->HasFastSmiOrObjectElements() || obj->HasFastDoubleElements() || 1809 obj->HasFastArgumentsElements() || 1810 obj->HasFastStringWrapperElements()); 1811 Handle<BackingStore> backing_store = Handle<BackingStore>::cast(store); 1812 if (!obj->IsJSArray() && 1813 entry == static_cast<uint32_t>(store->length()) - 1) { 1814 DeleteAtEnd(obj, backing_store, entry); 1815 return; 1816 } 1817 1818 Isolate* isolate = obj->GetIsolate(); 1819 backing_store->set_the_hole(entry); 1820 1821 // TODO(verwaest): Move this out of elements.cc. 1822 // If an old space backing store is larger than a certain size and 1823 // has too few used values, normalize it. 1824 // To avoid doing the check on every delete we require at least 1825 // one adjacent hole to the value being deleted. 1826 const int kMinLengthForSparsenessCheck = 64; 1827 if (backing_store->length() < kMinLengthForSparsenessCheck) return; 1828 if (backing_store->GetHeap()->InNewSpace(*backing_store)) return; 1829 uint32_t length = 0; 1830 if (obj->IsJSArray()) { 1831 JSArray::cast(*obj)->length()->ToArrayLength(&length); 1832 } else { 1833 length = static_cast<uint32_t>(store->length()); 1834 } 1835 if ((entry > 0 && backing_store->is_the_hole(isolate, entry - 1)) || 1836 (entry + 1 < length && 1837 backing_store->is_the_hole(isolate, entry + 1))) { 1838 if (!obj->IsJSArray()) { 1839 uint32_t i; 1840 for (i = entry + 1; i < length; i++) { 1841 if (!backing_store->is_the_hole(isolate, i)) break; 1842 } 1843 if (i == length) { 1844 DeleteAtEnd(obj, backing_store, entry); 1845 return; 1846 } 1847 } 1848 int num_used = 0; 1849 for (int i = 0; i < backing_store->length(); ++i) { 1850 if (!backing_store->is_the_hole(isolate, i)) { 1851 ++num_used; 1852 // Bail out if a number dictionary wouldn't be able to save at least 1853 // 75% space. 1854 if (4 * SeededNumberDictionary::ComputeCapacity(num_used) * 1855 SeededNumberDictionary::kEntrySize > 1856 backing_store->length()) { 1857 return; 1858 } 1859 } 1860 } 1861 JSObject::NormalizeElements(obj); 1862 } 1863 } 1864 1865 static void ReconfigureImpl(Handle<JSObject> object, 1866 Handle<FixedArrayBase> store, uint32_t entry, 1867 Handle<Object> value, 1868 PropertyAttributes attributes) { 1869 Handle<SeededNumberDictionary> dictionary = 1870 JSObject::NormalizeElements(object); 1871 entry = dictionary->FindEntry(entry); 1872 DictionaryElementsAccessor::ReconfigureImpl(object, dictionary, entry, 1873 value, attributes); 1874 } 1875 1876 static void AddImpl(Handle<JSObject> object, uint32_t index, 1877 Handle<Object> value, PropertyAttributes attributes, 1878 uint32_t new_capacity) { 1879 DCHECK_EQ(NONE, attributes); 1880 ElementsKind from_kind = object->GetElementsKind(); 1881 ElementsKind to_kind = Subclass::kind(); 1882 if (IsDictionaryElementsKind(from_kind) || 1883 IsFastDoubleElementsKind(from_kind) != 1884 IsFastDoubleElementsKind(to_kind) || 1885 Subclass::GetCapacityImpl(*object, object->elements()) != 1886 new_capacity) { 1887 Subclass::GrowCapacityAndConvertImpl(object, new_capacity); 1888 } else { 1889 if (IsFastElementsKind(from_kind) && from_kind != to_kind) { 1890 JSObject::TransitionElementsKind(object, to_kind); 1891 } 1892 if (IsFastSmiOrObjectElementsKind(from_kind)) { 1893 DCHECK(IsFastSmiOrObjectElementsKind(to_kind)); 1894 JSObject::EnsureWritableFastElements(object); 1895 } 1896 } 1897 Subclass::SetImpl(object, index, *value); 1898 } 1899 1900 static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) { 1901 ElementsKind kind = KindTraits::Kind; 1902 if (IsFastPackedElementsKind(kind)) { 1903 JSObject::TransitionElementsKind(obj, GetHoleyElementsKind(kind)); 1904 } 1905 if (IsFastSmiOrObjectElementsKind(KindTraits::Kind)) { 1906 JSObject::EnsureWritableFastElements(obj); 1907 } 1908 DeleteCommon(obj, entry, handle(obj->elements())); 1909 } 1910 1911 static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* backing_store, 1912 uint32_t entry) { 1913 return !BackingStore::cast(backing_store)->is_the_hole(isolate, entry); 1914 } 1915 1916 static uint32_t NumberOfElementsImpl(JSObject* receiver, 1917 FixedArrayBase* backing_store) { 1918 uint32_t max_index = Subclass::GetMaxIndex(receiver, backing_store); 1919 if (IsFastPackedElementsKind(Subclass::kind())) return max_index; 1920 Isolate* isolate = receiver->GetIsolate(); 1921 uint32_t count = 0; 1922 for (uint32_t i = 0; i < max_index; i++) { 1923 if (Subclass::HasEntryImpl(isolate, backing_store, i)) count++; 1924 } 1925 return count; 1926 } 1927 1928 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver, 1929 KeyAccumulator* accumulator, 1930 AddKeyConversion convert) { 1931 Isolate* isolate = accumulator->isolate(); 1932 Handle<FixedArrayBase> elements(receiver->elements(), isolate); 1933 uint32_t length = Subclass::GetMaxNumberOfEntries(*receiver, *elements); 1934 for (uint32_t i = 0; i < length; i++) { 1935 if (IsFastPackedElementsKind(KindTraits::Kind) || 1936 HasEntryImpl(isolate, *elements, i)) { 1937 accumulator->AddKey(Subclass::GetImpl(*elements, i), convert); 1938 } 1939 } 1940 } 1941 1942 static void ValidateContents(Handle<JSObject> holder, int length) { 1943 #if DEBUG 1944 Isolate* isolate = holder->GetIsolate(); 1945 Heap* heap = isolate->heap(); 1946 HandleScope scope(isolate); 1947 Handle<FixedArrayBase> elements(holder->elements(), isolate); 1948 Map* map = elements->map(); 1949 if (IsFastSmiOrObjectElementsKind(KindTraits::Kind)) { 1950 DCHECK_NE(map, heap->fixed_double_array_map()); 1951 } else if (IsFastDoubleElementsKind(KindTraits::Kind)) { 1952 DCHECK_NE(map, heap->fixed_cow_array_map()); 1953 if (map == heap->fixed_array_map()) DCHECK_EQ(0, length); 1954 } else { 1955 UNREACHABLE(); 1956 } 1957 if (length == 0) return; // nothing to do! 1958 #if ENABLE_SLOW_DCHECKS 1959 DisallowHeapAllocation no_gc; 1960 Handle<BackingStore> backing_store = Handle<BackingStore>::cast(elements); 1961 if (IsFastSmiElementsKind(KindTraits::Kind)) { 1962 for (int i = 0; i < length; i++) { 1963 DCHECK(BackingStore::get(*backing_store, i, isolate)->IsSmi() || 1964 (IsFastHoleyElementsKind(KindTraits::Kind) && 1965 backing_store->is_the_hole(isolate, i))); 1966 } 1967 } else if (KindTraits::Kind == FAST_ELEMENTS || 1968 KindTraits::Kind == FAST_DOUBLE_ELEMENTS) { 1969 for (int i = 0; i < length; i++) { 1970 DCHECK(!backing_store->is_the_hole(isolate, i)); 1971 } 1972 } else { 1973 DCHECK(IsFastHoleyElementsKind(KindTraits::Kind)); 1974 } 1975 #endif 1976 #endif 1977 } 1978 1979 static Handle<Object> PopImpl(Handle<JSArray> receiver) { 1980 return Subclass::RemoveElement(receiver, AT_END); 1981 } 1982 1983 static Handle<Object> ShiftImpl(Handle<JSArray> receiver) { 1984 return Subclass::RemoveElement(receiver, AT_START); 1985 } 1986 1987 static uint32_t PushImpl(Handle<JSArray> receiver, 1988 Arguments* args, uint32_t push_size) { 1989 Handle<FixedArrayBase> backing_store(receiver->elements()); 1990 return Subclass::AddArguments(receiver, backing_store, args, push_size, 1991 AT_END); 1992 } 1993 1994 static uint32_t UnshiftImpl(Handle<JSArray> receiver, 1995 Arguments* args, uint32_t unshift_size) { 1996 Handle<FixedArrayBase> backing_store(receiver->elements()); 1997 return Subclass::AddArguments(receiver, backing_store, args, unshift_size, 1998 AT_START); 1999 } 2000 2001 static Handle<JSArray> SliceImpl(Handle<JSObject> receiver, 2002 uint32_t start, uint32_t end) { 2003 Isolate* isolate = receiver->GetIsolate(); 2004 Handle<FixedArrayBase> backing_store(receiver->elements(), isolate); 2005 int result_len = end < start ? 0u : end - start; 2006 Handle<JSArray> result_array = isolate->factory()->NewJSArray( 2007 KindTraits::Kind, result_len, result_len); 2008 DisallowHeapAllocation no_gc; 2009 Subclass::CopyElementsImpl(*backing_store, start, result_array->elements(), 2010 KindTraits::Kind, 0, kPackedSizeNotKnown, 2011 result_len); 2012 Subclass::TryTransitionResultArrayToPacked(result_array); 2013 return result_array; 2014 } 2015 2016 static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver, 2017 uint32_t start, uint32_t delete_count, 2018 Arguments* args, uint32_t add_count) { 2019 Isolate* isolate = receiver->GetIsolate(); 2020 Heap* heap = isolate->heap(); 2021 uint32_t length = Smi::cast(receiver->length())->value(); 2022 uint32_t new_length = length - delete_count + add_count; 2023 2024 ElementsKind kind = KindTraits::Kind; 2025 if (new_length <= static_cast<uint32_t>(receiver->elements()->length()) && 2026 IsFastSmiOrObjectElementsKind(kind)) { 2027 HandleScope scope(isolate); 2028 JSObject::EnsureWritableFastElements(receiver); 2029 } 2030 2031 Handle<FixedArrayBase> backing_store(receiver->elements(), isolate); 2032 2033 if (new_length == 0) { 2034 receiver->set_elements(heap->empty_fixed_array()); 2035 receiver->set_length(Smi::kZero); 2036 return isolate->factory()->NewJSArrayWithElements( 2037 backing_store, KindTraits::Kind, delete_count); 2038 } 2039 2040 // Construct the result array which holds the deleted elements. 2041 Handle<JSArray> deleted_elements = isolate->factory()->NewJSArray( 2042 KindTraits::Kind, delete_count, delete_count); 2043 if (delete_count > 0) { 2044 DisallowHeapAllocation no_gc; 2045 Subclass::CopyElementsImpl(*backing_store, start, 2046 deleted_elements->elements(), KindTraits::Kind, 2047 0, kPackedSizeNotKnown, delete_count); 2048 } 2049 2050 // Delete and move elements to make space for add_count new elements. 2051 if (add_count < delete_count) { 2052 Subclass::SpliceShrinkStep(isolate, receiver, backing_store, start, 2053 delete_count, add_count, length, new_length); 2054 } else if (add_count > delete_count) { 2055 backing_store = 2056 Subclass::SpliceGrowStep(isolate, receiver, backing_store, start, 2057 delete_count, add_count, length, new_length); 2058 } 2059 2060 // Copy over the arguments. 2061 Subclass::CopyArguments(args, backing_store, add_count, 3, start); 2062 2063 receiver->set_length(Smi::FromInt(new_length)); 2064 Subclass::TryTransitionResultArrayToPacked(deleted_elements); 2065 return deleted_elements; 2066 } 2067 2068 static Maybe<bool> CollectValuesOrEntriesImpl( 2069 Isolate* isolate, Handle<JSObject> object, 2070 Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items, 2071 PropertyFilter filter) { 2072 Handle<BackingStore> elements(BackingStore::cast(object->elements()), 2073 isolate); 2074 int count = 0; 2075 uint32_t length = elements->length(); 2076 for (uint32_t index = 0; index < length; ++index) { 2077 if (!HasEntryImpl(isolate, *elements, index)) continue; 2078 Handle<Object> value = Subclass::GetImpl(*elements, index); 2079 if (get_entries) { 2080 value = MakeEntryPair(isolate, index, value); 2081 } 2082 values_or_entries->set(count++, *value); 2083 } 2084 *nof_items = count; 2085 return Just(true); 2086 } 2087 2088 static void MoveElements(Isolate* isolate, Handle<JSArray> receiver, 2089 Handle<FixedArrayBase> backing_store, int dst_index, 2090 int src_index, int len, int hole_start, 2091 int hole_end) { 2092 Heap* heap = isolate->heap(); 2093 Handle<BackingStore> dst_elms = Handle<BackingStore>::cast(backing_store); 2094 if (heap->CanMoveObjectStart(*dst_elms) && dst_index == 0) { 2095 // Update all the copies of this backing_store handle. 2096 *dst_elms.location() = 2097 BackingStore::cast(heap->LeftTrimFixedArray(*dst_elms, src_index)); 2098 receiver->set_elements(*dst_elms); 2099 // Adjust the hole offset as the array has been shrunk. 2100 hole_end -= src_index; 2101 DCHECK_LE(hole_start, backing_store->length()); 2102 DCHECK_LE(hole_end, backing_store->length()); 2103 } else if (len != 0) { 2104 if (IsFastDoubleElementsKind(KindTraits::Kind)) { 2105 MemMove(dst_elms->data_start() + dst_index, 2106 dst_elms->data_start() + src_index, len * kDoubleSize); 2107 } else { 2108 DisallowHeapAllocation no_gc; 2109 heap->MoveElements(FixedArray::cast(*dst_elms), dst_index, src_index, 2110 len); 2111 } 2112 } 2113 if (hole_start != hole_end) { 2114 dst_elms->FillWithHoles(hole_start, hole_end); 2115 } 2116 } 2117 2118 static Maybe<bool> IncludesValueImpl(Isolate* isolate, 2119 Handle<JSObject> receiver, 2120 Handle<Object> search_value, 2121 uint32_t start_from, uint32_t length) { 2122 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver)); 2123 DisallowHeapAllocation no_gc; 2124 FixedArrayBase* elements_base = receiver->elements(); 2125 Object* the_hole = isolate->heap()->the_hole_value(); 2126 Object* undefined = isolate->heap()->undefined_value(); 2127 Object* value = *search_value; 2128 2129 // Elements beyond the capacity of the backing store treated as undefined. 2130 if (value == undefined && 2131 static_cast<uint32_t>(elements_base->length()) < length) { 2132 return Just(true); 2133 } 2134 2135 if (start_from >= length) return Just(false); 2136 2137 length = std::min(static_cast<uint32_t>(elements_base->length()), length); 2138 2139 if (!value->IsNumber()) { 2140 if (value == undefined) { 2141 // Only FAST_ELEMENTS, FAST_HOLEY_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, and 2142 // FAST_HOLEY_DOUBLE_ELEMENTS can have `undefined` as a value. 2143 if (!IsFastObjectElementsKind(Subclass::kind()) && 2144 !IsFastHoleyElementsKind(Subclass::kind())) { 2145 return Just(false); 2146 } 2147 2148 // Search for `undefined` or The Hole in FAST_ELEMENTS, 2149 // FAST_HOLEY_ELEMENTS or FAST_HOLEY_SMI_ELEMENTS 2150 if (IsFastSmiOrObjectElementsKind(Subclass::kind())) { 2151 auto elements = FixedArray::cast(receiver->elements()); 2152 2153 for (uint32_t k = start_from; k < length; ++k) { 2154 Object* element_k = elements->get(k); 2155 2156 if (IsFastHoleyElementsKind(Subclass::kind()) && 2157 element_k == the_hole) { 2158 return Just(true); 2159 } 2160 if (IsFastObjectElementsKind(Subclass::kind()) && 2161 element_k == undefined) { 2162 return Just(true); 2163 } 2164 } 2165 return Just(false); 2166 } else { 2167 // Seach for The Hole in FAST_HOLEY_DOUBLE_ELEMENTS 2168 DCHECK_EQ(Subclass::kind(), FAST_HOLEY_DOUBLE_ELEMENTS); 2169 auto elements = FixedDoubleArray::cast(receiver->elements()); 2170 2171 for (uint32_t k = start_from; k < length; ++k) { 2172 if (IsFastHoleyElementsKind(Subclass::kind()) && 2173 elements->is_the_hole(k)) { 2174 return Just(true); 2175 } 2176 } 2177 return Just(false); 2178 } 2179 } else if (!IsFastObjectElementsKind(Subclass::kind())) { 2180 // Search for non-number, non-Undefined value, with either 2181 // FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS or 2182 // FAST_HOLEY_DOUBLE_ELEMENTS. Guaranteed to return false, since these 2183 // elements kinds can only contain Number values or undefined. 2184 return Just(false); 2185 } else { 2186 // Search for non-number, non-Undefined value with either 2187 // FAST_ELEMENTS or FAST_HOLEY_ELEMENTS. 2188 DCHECK(IsFastObjectElementsKind(Subclass::kind())); 2189 auto elements = FixedArray::cast(receiver->elements()); 2190 2191 for (uint32_t k = start_from; k < length; ++k) { 2192 Object* element_k = elements->get(k); 2193 if (IsFastHoleyElementsKind(Subclass::kind()) && 2194 element_k == the_hole) { 2195 continue; 2196 } 2197 2198 if (value->SameValueZero(element_k)) return Just(true); 2199 } 2200 return Just(false); 2201 } 2202 } else { 2203 if (!value->IsNaN()) { 2204 double search_value = value->Number(); 2205 if (IsFastDoubleElementsKind(Subclass::kind())) { 2206 // Search for non-NaN Number in FAST_DOUBLE_ELEMENTS or 2207 // FAST_HOLEY_DOUBLE_ELEMENTS --- Skip TheHole, and trust UCOMISD or 2208 // similar operation for result. 2209 auto elements = FixedDoubleArray::cast(receiver->elements()); 2210 2211 for (uint32_t k = start_from; k < length; ++k) { 2212 if (IsFastHoleyElementsKind(Subclass::kind()) && 2213 elements->is_the_hole(k)) { 2214 continue; 2215 } 2216 if (elements->get_scalar(k) == search_value) return Just(true); 2217 } 2218 return Just(false); 2219 } else { 2220 // Search for non-NaN Number in FAST_ELEMENTS, FAST_HOLEY_ELEMENTS, 2221 // FAST_SMI_ELEMENTS or FAST_HOLEY_SMI_ELEMENTS --- Skip non-Numbers, 2222 // and trust UCOMISD or similar operation for result 2223 auto elements = FixedArray::cast(receiver->elements()); 2224 2225 for (uint32_t k = start_from; k < length; ++k) { 2226 Object* element_k = elements->get(k); 2227 if (element_k->IsNumber() && element_k->Number() == search_value) { 2228 return Just(true); 2229 } 2230 } 2231 return Just(false); 2232 } 2233 } else { 2234 // Search for NaN --- NaN cannot be represented with Smi elements, so 2235 // abort if ElementsKind is FAST_SMI_ELEMENTS or FAST_HOLEY_SMI_ELEMENTS 2236 if (IsFastSmiElementsKind(Subclass::kind())) return Just(false); 2237 2238 if (IsFastDoubleElementsKind(Subclass::kind())) { 2239 // Search for NaN in FAST_DOUBLE_ELEMENTS or 2240 // FAST_HOLEY_DOUBLE_ELEMENTS --- Skip The Hole and trust 2241 // std::isnan(elementK) for result 2242 auto elements = FixedDoubleArray::cast(receiver->elements()); 2243 2244 for (uint32_t k = start_from; k < length; ++k) { 2245 if (IsFastHoleyElementsKind(Subclass::kind()) && 2246 elements->is_the_hole(k)) { 2247 continue; 2248 } 2249 if (std::isnan(elements->get_scalar(k))) return Just(true); 2250 } 2251 return Just(false); 2252 } else { 2253 // Search for NaN in FAST_ELEMENTS, FAST_HOLEY_ELEMENTS, 2254 // FAST_SMI_ELEMENTS or FAST_HOLEY_SMI_ELEMENTS. Return true if 2255 // elementK->IsHeapNumber() && std::isnan(elementK->Number()) 2256 DCHECK(IsFastSmiOrObjectElementsKind(Subclass::kind())); 2257 auto elements = FixedArray::cast(receiver->elements()); 2258 2259 for (uint32_t k = start_from; k < length; ++k) { 2260 if (elements->get(k)->IsNaN()) return Just(true); 2261 } 2262 return Just(false); 2263 } 2264 } 2265 } 2266 } 2267 2268 private: 2269 // SpliceShrinkStep might modify the backing_store. 2270 static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver, 2271 Handle<FixedArrayBase> backing_store, 2272 uint32_t start, uint32_t delete_count, 2273 uint32_t add_count, uint32_t len, 2274 uint32_t new_length) { 2275 const int move_left_count = len - delete_count - start; 2276 const int move_left_dst_index = start + add_count; 2277 Subclass::MoveElements(isolate, receiver, backing_store, 2278 move_left_dst_index, start + delete_count, 2279 move_left_count, new_length, len); 2280 } 2281 2282 // SpliceGrowStep might modify the backing_store. 2283 static Handle<FixedArrayBase> SpliceGrowStep( 2284 Isolate* isolate, Handle<JSArray> receiver, 2285 Handle<FixedArrayBase> backing_store, uint32_t start, 2286 uint32_t delete_count, uint32_t add_count, uint32_t length, 2287 uint32_t new_length) { 2288 // Check we do not overflow the new_length. 2289 DCHECK((add_count - delete_count) <= (Smi::kMaxValue - length)); 2290 // Check if backing_store is big enough. 2291 if (new_length <= static_cast<uint32_t>(backing_store->length())) { 2292 Subclass::MoveElements(isolate, receiver, backing_store, 2293 start + add_count, start + delete_count, 2294 (length - delete_count - start), 0, 0); 2295 // MoveElements updates the backing_store in-place. 2296 return backing_store; 2297 } 2298 // New backing storage is needed. 2299 int capacity = JSObject::NewElementsCapacity(new_length); 2300 // Partially copy all elements up to start. 2301 Handle<FixedArrayBase> new_elms = Subclass::ConvertElementsWithCapacity( 2302 receiver, backing_store, KindTraits::Kind, capacity, start); 2303 // Copy the trailing elements after start + delete_count 2304 Subclass::CopyElementsImpl(*backing_store, start + delete_count, *new_elms, 2305 KindTraits::Kind, start + add_count, 2306 kPackedSizeNotKnown, 2307 ElementsAccessor::kCopyToEndAndInitializeToHole); 2308 receiver->set_elements(*new_elms); 2309 return new_elms; 2310 } 2311 2312 static Handle<Object> RemoveElement(Handle<JSArray> receiver, 2313 Where remove_position) { 2314 Isolate* isolate = receiver->GetIsolate(); 2315 ElementsKind kind = KindTraits::Kind; 2316 if (IsFastSmiOrObjectElementsKind(kind)) { 2317 HandleScope scope(isolate); 2318 JSObject::EnsureWritableFastElements(receiver); 2319 } 2320 Handle<FixedArrayBase> backing_store(receiver->elements(), isolate); 2321 uint32_t length = 2322 static_cast<uint32_t>(Smi::cast(receiver->length())->value()); 2323 DCHECK(length > 0); 2324 int new_length = length - 1; 2325 int remove_index = remove_position == AT_START ? 0 : new_length; 2326 Handle<Object> result = Subclass::GetImpl(*backing_store, remove_index); 2327 if (remove_position == AT_START) { 2328 Subclass::MoveElements(isolate, receiver, backing_store, 0, 1, new_length, 2329 0, 0); 2330 } 2331 Subclass::SetLengthImpl(isolate, receiver, new_length, backing_store); 2332 2333 if (IsHoleyElementsKind(kind) && result->IsTheHole(isolate)) { 2334 return isolate->factory()->undefined_value(); 2335 } 2336 return result; 2337 } 2338 2339 static uint32_t AddArguments(Handle<JSArray> receiver, 2340 Handle<FixedArrayBase> backing_store, 2341 Arguments* args, uint32_t add_size, 2342 Where add_position) { 2343 uint32_t length = Smi::cast(receiver->length())->value(); 2344 DCHECK(0 < add_size); 2345 uint32_t elms_len = backing_store->length(); 2346 // Check we do not overflow the new_length. 2347 DCHECK(add_size <= static_cast<uint32_t>(Smi::kMaxValue - length)); 2348 uint32_t new_length = length + add_size; 2349 2350 if (new_length > elms_len) { 2351 // New backing storage is needed. 2352 uint32_t capacity = JSObject::NewElementsCapacity(new_length); 2353 // If we add arguments to the start we have to shift the existing objects. 2354 int copy_dst_index = add_position == AT_START ? add_size : 0; 2355 // Copy over all objects to a new backing_store. 2356 backing_store = Subclass::ConvertElementsWithCapacity( 2357 receiver, backing_store, KindTraits::Kind, capacity, 0, 2358 copy_dst_index, ElementsAccessor::kCopyToEndAndInitializeToHole); 2359 receiver->set_elements(*backing_store); 2360 } else if (add_position == AT_START) { 2361 // If the backing store has enough capacity and we add elements to the 2362 // start we have to shift the existing objects. 2363 Isolate* isolate = receiver->GetIsolate(); 2364 Subclass::MoveElements(isolate, receiver, backing_store, add_size, 0, 2365 length, 0, 0); 2366 } 2367 2368 int insertion_index = add_position == AT_START ? 0 : length; 2369 // Copy the arguments to the start. 2370 Subclass::CopyArguments(args, backing_store, add_size, 1, insertion_index); 2371 // Set the length. 2372 receiver->set_length(Smi::FromInt(new_length)); 2373 return new_length; 2374 } 2375 2376 static void CopyArguments(Arguments* args, Handle<FixedArrayBase> dst_store, 2377 uint32_t copy_size, uint32_t src_index, 2378 uint32_t dst_index) { 2379 // Add the provided values. 2380 DisallowHeapAllocation no_gc; 2381 FixedArrayBase* raw_backing_store = *dst_store; 2382 WriteBarrierMode mode = raw_backing_store->GetWriteBarrierMode(no_gc); 2383 for (uint32_t i = 0; i < copy_size; i++) { 2384 Object* argument = (*args)[src_index + i]; 2385 DCHECK(!argument->IsTheHole(raw_backing_store->GetIsolate())); 2386 Subclass::SetImpl(raw_backing_store, dst_index + i, argument, mode); 2387 } 2388 } 2389 }; 2390 2391 template <typename Subclass, typename KindTraits> 2392 class FastSmiOrObjectElementsAccessor 2393 : public FastElementsAccessor<Subclass, KindTraits> { 2394 public: 2395 explicit FastSmiOrObjectElementsAccessor(const char* name) 2396 : FastElementsAccessor<Subclass, KindTraits>(name) {} 2397 2398 static inline void SetImpl(Handle<JSObject> holder, uint32_t entry, 2399 Object* value) { 2400 SetImpl(holder->elements(), entry, value); 2401 } 2402 2403 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 2404 Object* value) { 2405 FixedArray::cast(backing_store)->set(entry, value); 2406 } 2407 2408 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 2409 Object* value, WriteBarrierMode mode) { 2410 FixedArray::cast(backing_store)->set(entry, value, mode); 2411 } 2412 2413 static Object* GetRaw(FixedArray* backing_store, uint32_t entry) { 2414 uint32_t index = Subclass::GetIndexForEntryImpl(backing_store, entry); 2415 return backing_store->get(index); 2416 } 2417 2418 // NOTE: this method violates the handlified function signature convention: 2419 // raw pointer parameters in the function that allocates. 2420 // See ElementsAccessor::CopyElements() for details. 2421 // This method could actually allocate if copying from double elements to 2422 // object elements. 2423 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 2424 FixedArrayBase* to, ElementsKind from_kind, 2425 uint32_t to_start, int packed_size, 2426 int copy_size) { 2427 DisallowHeapAllocation no_gc; 2428 ElementsKind to_kind = KindTraits::Kind; 2429 switch (from_kind) { 2430 case FAST_SMI_ELEMENTS: 2431 case FAST_HOLEY_SMI_ELEMENTS: 2432 case FAST_ELEMENTS: 2433 case FAST_HOLEY_ELEMENTS: 2434 CopyObjectToObjectElements(from, from_kind, from_start, to, to_kind, 2435 to_start, copy_size); 2436 break; 2437 case FAST_DOUBLE_ELEMENTS: 2438 case FAST_HOLEY_DOUBLE_ELEMENTS: { 2439 AllowHeapAllocation allow_allocation; 2440 DCHECK(IsFastObjectElementsKind(to_kind)); 2441 CopyDoubleToObjectElements(from, from_start, to, to_start, copy_size); 2442 break; 2443 } 2444 case DICTIONARY_ELEMENTS: 2445 CopyDictionaryToObjectElements(from, from_start, to, to_kind, to_start, 2446 copy_size); 2447 break; 2448 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: 2449 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: 2450 case FAST_STRING_WRAPPER_ELEMENTS: 2451 case SLOW_STRING_WRAPPER_ELEMENTS: 2452 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS: 2453 TYPED_ARRAYS(TYPED_ARRAY_CASE) 2454 #undef TYPED_ARRAY_CASE 2455 // This function is currently only used for JSArrays with non-zero 2456 // length. 2457 UNREACHABLE(); 2458 break; 2459 case NO_ELEMENTS: 2460 break; // Nothing to do. 2461 } 2462 } 2463 2464 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate, 2465 Handle<JSObject> receiver, 2466 Handle<Object> search_value, 2467 uint32_t start_from, uint32_t length) { 2468 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver)); 2469 DisallowHeapAllocation no_gc; 2470 FixedArrayBase* elements_base = receiver->elements(); 2471 Object* value = *search_value; 2472 2473 if (start_from >= length) return Just<int64_t>(-1); 2474 2475 length = std::min(static_cast<uint32_t>(elements_base->length()), length); 2476 2477 // Only FAST_{,HOLEY_}ELEMENTS can store non-numbers. 2478 if (!value->IsNumber() && !IsFastObjectElementsKind(Subclass::kind())) { 2479 return Just<int64_t>(-1); 2480 } 2481 // NaN can never be found by strict equality. 2482 if (value->IsNaN()) return Just<int64_t>(-1); 2483 2484 FixedArray* elements = FixedArray::cast(receiver->elements()); 2485 for (uint32_t k = start_from; k < length; ++k) { 2486 if (value->StrictEquals(elements->get(k))) return Just<int64_t>(k); 2487 } 2488 return Just<int64_t>(-1); 2489 } 2490 }; 2491 2492 2493 class FastPackedSmiElementsAccessor 2494 : public FastSmiOrObjectElementsAccessor< 2495 FastPackedSmiElementsAccessor, 2496 ElementsKindTraits<FAST_SMI_ELEMENTS> > { 2497 public: 2498 explicit FastPackedSmiElementsAccessor(const char* name) 2499 : FastSmiOrObjectElementsAccessor< 2500 FastPackedSmiElementsAccessor, 2501 ElementsKindTraits<FAST_SMI_ELEMENTS> >(name) {} 2502 }; 2503 2504 2505 class FastHoleySmiElementsAccessor 2506 : public FastSmiOrObjectElementsAccessor< 2507 FastHoleySmiElementsAccessor, 2508 ElementsKindTraits<FAST_HOLEY_SMI_ELEMENTS> > { 2509 public: 2510 explicit FastHoleySmiElementsAccessor(const char* name) 2511 : FastSmiOrObjectElementsAccessor< 2512 FastHoleySmiElementsAccessor, 2513 ElementsKindTraits<FAST_HOLEY_SMI_ELEMENTS> >(name) {} 2514 }; 2515 2516 2517 class FastPackedObjectElementsAccessor 2518 : public FastSmiOrObjectElementsAccessor< 2519 FastPackedObjectElementsAccessor, 2520 ElementsKindTraits<FAST_ELEMENTS> > { 2521 public: 2522 explicit FastPackedObjectElementsAccessor(const char* name) 2523 : FastSmiOrObjectElementsAccessor< 2524 FastPackedObjectElementsAccessor, 2525 ElementsKindTraits<FAST_ELEMENTS> >(name) {} 2526 }; 2527 2528 2529 class FastHoleyObjectElementsAccessor 2530 : public FastSmiOrObjectElementsAccessor< 2531 FastHoleyObjectElementsAccessor, 2532 ElementsKindTraits<FAST_HOLEY_ELEMENTS> > { 2533 public: 2534 explicit FastHoleyObjectElementsAccessor(const char* name) 2535 : FastSmiOrObjectElementsAccessor< 2536 FastHoleyObjectElementsAccessor, 2537 ElementsKindTraits<FAST_HOLEY_ELEMENTS> >(name) {} 2538 }; 2539 2540 template <typename Subclass, typename KindTraits> 2541 class FastDoubleElementsAccessor 2542 : public FastElementsAccessor<Subclass, KindTraits> { 2543 public: 2544 explicit FastDoubleElementsAccessor(const char* name) 2545 : FastElementsAccessor<Subclass, KindTraits>(name) {} 2546 2547 static Handle<Object> GetImpl(Handle<JSObject> holder, uint32_t entry) { 2548 return GetImpl(holder->elements(), entry); 2549 } 2550 2551 static Handle<Object> GetImpl(FixedArrayBase* backing_store, uint32_t entry) { 2552 Isolate* isolate = backing_store->GetIsolate(); 2553 return FixedDoubleArray::get(FixedDoubleArray::cast(backing_store), entry, 2554 isolate); 2555 } 2556 2557 static inline void SetImpl(Handle<JSObject> holder, uint32_t entry, 2558 Object* value) { 2559 SetImpl(holder->elements(), entry, value); 2560 } 2561 2562 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 2563 Object* value) { 2564 FixedDoubleArray::cast(backing_store)->set(entry, value->Number()); 2565 } 2566 2567 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 2568 Object* value, WriteBarrierMode mode) { 2569 FixedDoubleArray::cast(backing_store)->set(entry, value->Number()); 2570 } 2571 2572 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 2573 FixedArrayBase* to, ElementsKind from_kind, 2574 uint32_t to_start, int packed_size, 2575 int copy_size) { 2576 DisallowHeapAllocation no_allocation; 2577 switch (from_kind) { 2578 case FAST_SMI_ELEMENTS: 2579 CopyPackedSmiToDoubleElements(from, from_start, to, to_start, 2580 packed_size, copy_size); 2581 break; 2582 case FAST_HOLEY_SMI_ELEMENTS: 2583 CopySmiToDoubleElements(from, from_start, to, to_start, copy_size); 2584 break; 2585 case FAST_DOUBLE_ELEMENTS: 2586 case FAST_HOLEY_DOUBLE_ELEMENTS: 2587 CopyDoubleToDoubleElements(from, from_start, to, to_start, copy_size); 2588 break; 2589 case FAST_ELEMENTS: 2590 case FAST_HOLEY_ELEMENTS: 2591 CopyObjectToDoubleElements(from, from_start, to, to_start, copy_size); 2592 break; 2593 case DICTIONARY_ELEMENTS: 2594 CopyDictionaryToDoubleElements(from, from_start, to, to_start, 2595 copy_size); 2596 break; 2597 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: 2598 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: 2599 case FAST_STRING_WRAPPER_ELEMENTS: 2600 case SLOW_STRING_WRAPPER_ELEMENTS: 2601 case NO_ELEMENTS: 2602 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS: 2603 TYPED_ARRAYS(TYPED_ARRAY_CASE) 2604 #undef TYPED_ARRAY_CASE 2605 // This function is currently only used for JSArrays with non-zero 2606 // length. 2607 UNREACHABLE(); 2608 break; 2609 } 2610 } 2611 2612 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate, 2613 Handle<JSObject> receiver, 2614 Handle<Object> search_value, 2615 uint32_t start_from, uint32_t length) { 2616 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver)); 2617 DisallowHeapAllocation no_gc; 2618 FixedArrayBase* elements_base = receiver->elements(); 2619 Object* value = *search_value; 2620 2621 if (start_from >= length) return Just<int64_t>(-1); 2622 2623 length = std::min(static_cast<uint32_t>(elements_base->length()), length); 2624 2625 if (!value->IsNumber()) { 2626 return Just<int64_t>(-1); 2627 } 2628 if (value->IsNaN()) { 2629 return Just<int64_t>(-1); 2630 } 2631 double numeric_search_value = value->Number(); 2632 FixedDoubleArray* elements = FixedDoubleArray::cast(receiver->elements()); 2633 2634 for (uint32_t k = start_from; k < length; ++k) { 2635 if (elements->is_the_hole(k)) { 2636 continue; 2637 } 2638 if (elements->get_scalar(k) == numeric_search_value) { 2639 return Just<int64_t>(k); 2640 } 2641 } 2642 return Just<int64_t>(-1); 2643 } 2644 }; 2645 2646 2647 class FastPackedDoubleElementsAccessor 2648 : public FastDoubleElementsAccessor< 2649 FastPackedDoubleElementsAccessor, 2650 ElementsKindTraits<FAST_DOUBLE_ELEMENTS> > { 2651 public: 2652 explicit FastPackedDoubleElementsAccessor(const char* name) 2653 : FastDoubleElementsAccessor< 2654 FastPackedDoubleElementsAccessor, 2655 ElementsKindTraits<FAST_DOUBLE_ELEMENTS> >(name) {} 2656 }; 2657 2658 2659 class FastHoleyDoubleElementsAccessor 2660 : public FastDoubleElementsAccessor< 2661 FastHoleyDoubleElementsAccessor, 2662 ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> > { 2663 public: 2664 explicit FastHoleyDoubleElementsAccessor(const char* name) 2665 : FastDoubleElementsAccessor< 2666 FastHoleyDoubleElementsAccessor, 2667 ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> >(name) {} 2668 }; 2669 2670 2671 // Super class for all external element arrays. 2672 template <ElementsKind Kind, typename ctype> 2673 class TypedElementsAccessor 2674 : public ElementsAccessorBase<TypedElementsAccessor<Kind, ctype>, 2675 ElementsKindTraits<Kind>> { 2676 public: 2677 explicit TypedElementsAccessor(const char* name) 2678 : ElementsAccessorBase<AccessorClass, 2679 ElementsKindTraits<Kind> >(name) {} 2680 2681 typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore; 2682 typedef TypedElementsAccessor<Kind, ctype> AccessorClass; 2683 2684 static inline void SetImpl(Handle<JSObject> holder, uint32_t entry, 2685 Object* value) { 2686 SetImpl(holder->elements(), entry, value); 2687 } 2688 2689 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 2690 Object* value) { 2691 BackingStore::cast(backing_store)->SetValue(entry, value); 2692 } 2693 2694 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 2695 Object* value, WriteBarrierMode mode) { 2696 BackingStore::cast(backing_store)->SetValue(entry, value); 2697 } 2698 2699 static Handle<Object> GetImpl(Handle<JSObject> holder, uint32_t entry) { 2700 return GetImpl(holder->elements(), entry); 2701 } 2702 2703 static Handle<Object> GetImpl(FixedArrayBase* backing_store, uint32_t entry) { 2704 return BackingStore::get(BackingStore::cast(backing_store), entry); 2705 } 2706 2707 static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) { 2708 return PropertyDetails(DONT_DELETE, DATA, 0, PropertyCellType::kNoCell); 2709 } 2710 2711 static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, 2712 uint32_t entry) { 2713 return PropertyDetails(DONT_DELETE, DATA, 0, PropertyCellType::kNoCell); 2714 } 2715 2716 static bool HasElementImpl(Isolate* isolate, Handle<JSObject> holder, 2717 uint32_t index, 2718 Handle<FixedArrayBase> backing_store, 2719 PropertyFilter filter) { 2720 return index < AccessorClass::GetCapacityImpl(*holder, *backing_store); 2721 } 2722 2723 static bool HasAccessorsImpl(JSObject* holder, 2724 FixedArrayBase* backing_store) { 2725 return false; 2726 } 2727 2728 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array, 2729 uint32_t length, 2730 Handle<FixedArrayBase> backing_store) { 2731 // External arrays do not support changing their length. 2732 UNREACHABLE(); 2733 } 2734 2735 static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) { 2736 UNREACHABLE(); 2737 } 2738 2739 static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store, 2740 uint32_t entry) { 2741 return entry; 2742 } 2743 2744 static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder, 2745 FixedArrayBase* backing_store, 2746 uint32_t index, PropertyFilter filter) { 2747 return index < AccessorClass::GetCapacityImpl(holder, backing_store) 2748 ? index 2749 : kMaxUInt32; 2750 } 2751 2752 static uint32_t GetCapacityImpl(JSObject* holder, 2753 FixedArrayBase* backing_store) { 2754 JSArrayBufferView* view = JSArrayBufferView::cast(holder); 2755 if (view->WasNeutered()) return 0; 2756 return backing_store->length(); 2757 } 2758 2759 static uint32_t NumberOfElementsImpl(JSObject* receiver, 2760 FixedArrayBase* backing_store) { 2761 return AccessorClass::GetCapacityImpl(receiver, backing_store); 2762 } 2763 2764 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver, 2765 KeyAccumulator* accumulator, 2766 AddKeyConversion convert) { 2767 Handle<FixedArrayBase> elements(receiver->elements()); 2768 uint32_t length = AccessorClass::GetCapacityImpl(*receiver, *elements); 2769 for (uint32_t i = 0; i < length; i++) { 2770 Handle<Object> value = AccessorClass::GetImpl(*elements, i); 2771 accumulator->AddKey(value, convert); 2772 } 2773 } 2774 2775 static Maybe<bool> CollectValuesOrEntriesImpl( 2776 Isolate* isolate, Handle<JSObject> object, 2777 Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items, 2778 PropertyFilter filter) { 2779 int count = 0; 2780 if ((filter & ONLY_CONFIGURABLE) == 0) { 2781 Handle<FixedArrayBase> elements(object->elements()); 2782 uint32_t length = AccessorClass::GetCapacityImpl(*object, *elements); 2783 for (uint32_t index = 0; index < length; ++index) { 2784 Handle<Object> value = AccessorClass::GetImpl(*elements, index); 2785 if (get_entries) { 2786 value = MakeEntryPair(isolate, index, value); 2787 } 2788 values_or_entries->set(count++, *value); 2789 } 2790 } 2791 *nof_items = count; 2792 return Just(true); 2793 } 2794 2795 static Maybe<bool> IncludesValueImpl(Isolate* isolate, 2796 Handle<JSObject> receiver, 2797 Handle<Object> value, 2798 uint32_t start_from, uint32_t length) { 2799 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver)); 2800 DisallowHeapAllocation no_gc; 2801 2802 BackingStore* elements = BackingStore::cast(receiver->elements()); 2803 if (value->IsUndefined(isolate) && 2804 length > static_cast<uint32_t>(elements->length())) { 2805 return Just(true); 2806 } 2807 if (!value->IsNumber()) return Just(false); 2808 2809 double search_value = value->Number(); 2810 2811 if (!std::isfinite(search_value)) { 2812 // Integral types cannot represent +Inf or NaN 2813 if (AccessorClass::kind() < FLOAT32_ELEMENTS || 2814 AccessorClass::kind() > FLOAT64_ELEMENTS) { 2815 return Just(false); 2816 } 2817 } else if (search_value < std::numeric_limits<ctype>::lowest() || 2818 search_value > std::numeric_limits<ctype>::max()) { 2819 // Return false if value can't be represented in this space 2820 return Just(false); 2821 } 2822 2823 // Prototype has no elements, and not searching for the hole --- limit 2824 // search to backing store length. 2825 if (static_cast<uint32_t>(elements->length()) < length) { 2826 length = elements->length(); 2827 } 2828 2829 if (!std::isnan(search_value)) { 2830 for (uint32_t k = start_from; k < length; ++k) { 2831 double element_k = elements->get_scalar(k); 2832 if (element_k == search_value) return Just(true); 2833 } 2834 return Just(false); 2835 } else { 2836 for (uint32_t k = start_from; k < length; ++k) { 2837 double element_k = elements->get_scalar(k); 2838 if (std::isnan(element_k)) return Just(true); 2839 } 2840 return Just(false); 2841 } 2842 } 2843 2844 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate, 2845 Handle<JSObject> receiver, 2846 Handle<Object> value, 2847 uint32_t start_from, uint32_t length) { 2848 DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver)); 2849 DisallowHeapAllocation no_gc; 2850 2851 BackingStore* elements = BackingStore::cast(receiver->elements()); 2852 if (!value->IsNumber()) return Just<int64_t>(-1); 2853 2854 double search_value = value->Number(); 2855 2856 if (!std::isfinite(search_value)) { 2857 // Integral types cannot represent +Inf or NaN. 2858 if (AccessorClass::kind() < FLOAT32_ELEMENTS || 2859 AccessorClass::kind() > FLOAT64_ELEMENTS) { 2860 return Just<int64_t>(-1); 2861 } 2862 } else if (search_value < std::numeric_limits<ctype>::lowest() || 2863 search_value > std::numeric_limits<ctype>::max()) { 2864 // Return false if value can't be represented in this ElementsKind. 2865 return Just<int64_t>(-1); 2866 } 2867 2868 // Prototype has no elements, and not searching for the hole --- limit 2869 // search to backing store length. 2870 if (static_cast<uint32_t>(elements->length()) < length) { 2871 length = elements->length(); 2872 } 2873 2874 if (std::isnan(search_value)) { 2875 return Just<int64_t>(-1); 2876 } 2877 2878 ctype typed_search_value = static_cast<ctype>(search_value); 2879 if (static_cast<double>(typed_search_value) != search_value) { 2880 return Just<int64_t>(-1); // Loss of precision. 2881 } 2882 2883 for (uint32_t k = start_from; k < length; ++k) { 2884 ctype element_k = elements->get_scalar(k); 2885 if (element_k == typed_search_value) return Just<int64_t>(k); 2886 } 2887 return Just<int64_t>(-1); 2888 } 2889 }; 2890 2891 #define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype, size) \ 2892 typedef TypedElementsAccessor<TYPE##_ELEMENTS, ctype> \ 2893 Fixed##Type##ElementsAccessor; 2894 2895 TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR) 2896 #undef FIXED_ELEMENTS_ACCESSOR 2897 2898 template <typename Subclass, typename ArgumentsAccessor, typename KindTraits> 2899 class SloppyArgumentsElementsAccessor 2900 : public ElementsAccessorBase<Subclass, KindTraits> { 2901 public: 2902 explicit SloppyArgumentsElementsAccessor(const char* name) 2903 : ElementsAccessorBase<Subclass, KindTraits>(name) { 2904 USE(KindTraits::Kind); 2905 } 2906 2907 static Handle<Object> GetImpl(Handle<JSObject> holder, uint32_t entry) { 2908 return GetImpl(holder->elements(), entry); 2909 } 2910 2911 static Handle<Object> GetImpl(FixedArrayBase* parameters, uint32_t entry) { 2912 Isolate* isolate = parameters->GetIsolate(); 2913 Handle<FixedArray> parameter_map(FixedArray::cast(parameters), isolate); 2914 uint32_t length = parameter_map->length() - 2; 2915 if (entry < length) { 2916 DisallowHeapAllocation no_gc; 2917 Object* probe = parameter_map->get(entry + 2); 2918 Context* context = Context::cast(parameter_map->get(0)); 2919 int context_entry = Smi::cast(probe)->value(); 2920 DCHECK(!context->get(context_entry)->IsTheHole(isolate)); 2921 return handle(context->get(context_entry), isolate); 2922 } else { 2923 // Object is not mapped, defer to the arguments. 2924 Handle<Object> result = ArgumentsAccessor::GetImpl( 2925 FixedArray::cast(parameter_map->get(1)), entry - length); 2926 // Elements of the arguments object in slow mode might be slow aliases. 2927 if (result->IsAliasedArgumentsEntry()) { 2928 DisallowHeapAllocation no_gc; 2929 AliasedArgumentsEntry* alias = AliasedArgumentsEntry::cast(*result); 2930 Context* context = Context::cast(parameter_map->get(0)); 2931 int context_entry = alias->aliased_context_slot(); 2932 DCHECK(!context->get(context_entry)->IsTheHole(isolate)); 2933 return handle(context->get(context_entry), isolate); 2934 } 2935 return result; 2936 } 2937 } 2938 2939 static void TransitionElementsKindImpl(Handle<JSObject> object, 2940 Handle<Map> map) { 2941 UNREACHABLE(); 2942 } 2943 2944 static void GrowCapacityAndConvertImpl(Handle<JSObject> object, 2945 uint32_t capacity) { 2946 UNREACHABLE(); 2947 } 2948 2949 static inline void SetImpl(Handle<JSObject> holder, uint32_t entry, 2950 Object* value) { 2951 SetImpl(holder->elements(), entry, value); 2952 } 2953 2954 static inline void SetImpl(FixedArrayBase* store, uint32_t entry, 2955 Object* value) { 2956 FixedArray* parameter_map = FixedArray::cast(store); 2957 uint32_t length = parameter_map->length() - 2; 2958 if (entry < length) { 2959 Object* probe = parameter_map->get(entry + 2); 2960 Context* context = Context::cast(parameter_map->get(0)); 2961 int context_entry = Smi::cast(probe)->value(); 2962 DCHECK(!context->get(context_entry)->IsTheHole(store->GetIsolate())); 2963 context->set(context_entry, value); 2964 } else { 2965 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 2966 Object* current = ArgumentsAccessor::GetRaw(arguments, entry - length); 2967 if (current->IsAliasedArgumentsEntry()) { 2968 AliasedArgumentsEntry* alias = AliasedArgumentsEntry::cast(current); 2969 Context* context = Context::cast(parameter_map->get(0)); 2970 int context_entry = alias->aliased_context_slot(); 2971 DCHECK(!context->get(context_entry)->IsTheHole(store->GetIsolate())); 2972 context->set(context_entry, value); 2973 } else { 2974 ArgumentsAccessor::SetImpl(arguments, entry - length, value); 2975 } 2976 } 2977 } 2978 2979 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array, 2980 uint32_t length, 2981 Handle<FixedArrayBase> parameter_map) { 2982 // Sloppy arguments objects are not arrays. 2983 UNREACHABLE(); 2984 } 2985 2986 static uint32_t GetCapacityImpl(JSObject* holder, 2987 FixedArrayBase* backing_store) { 2988 FixedArray* parameter_map = FixedArray::cast(backing_store); 2989 FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); 2990 return parameter_map->length() - 2 + 2991 ArgumentsAccessor::GetCapacityImpl(holder, arguments); 2992 } 2993 2994 static uint32_t GetMaxNumberOfEntries(JSObject* holder, 2995 FixedArrayBase* backing_store) { 2996 FixedArray* parameter_map = FixedArray::cast(backing_store); 2997 FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); 2998 return parameter_map->length() - 2 + 2999 ArgumentsAccessor::GetMaxNumberOfEntries(holder, arguments); 3000 } 3001 3002 static uint32_t NumberOfElementsImpl(JSObject* receiver, 3003 FixedArrayBase* backing_store) { 3004 FixedArray* parameter_map = FixedArray::cast(backing_store); 3005 FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); 3006 uint32_t nof_elements = 0; 3007 uint32_t length = parameter_map->length() - 2; 3008 for (uint32_t entry = 0; entry < length; entry++) { 3009 if (HasParameterMapArg(parameter_map, entry)) nof_elements++; 3010 } 3011 return nof_elements + 3012 ArgumentsAccessor::NumberOfElementsImpl(receiver, arguments); 3013 } 3014 3015 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver, 3016 KeyAccumulator* accumulator, 3017 AddKeyConversion convert) { 3018 Isolate* isolate = accumulator->isolate(); 3019 Handle<FixedArrayBase> elements(receiver->elements(), isolate); 3020 uint32_t length = GetCapacityImpl(*receiver, *elements); 3021 for (uint32_t entry = 0; entry < length; entry++) { 3022 if (!HasEntryImpl(isolate, *elements, entry)) continue; 3023 Handle<Object> value = GetImpl(*elements, entry); 3024 accumulator->AddKey(value, convert); 3025 } 3026 } 3027 3028 static bool HasEntryImpl(Isolate* isolate, FixedArrayBase* parameters, 3029 uint32_t entry) { 3030 FixedArray* parameter_map = FixedArray::cast(parameters); 3031 uint32_t length = parameter_map->length() - 2; 3032 if (entry < length) { 3033 return HasParameterMapArg(parameter_map, entry); 3034 } 3035 3036 FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); 3037 return ArgumentsAccessor::HasEntryImpl(isolate, arguments, entry - length); 3038 } 3039 3040 static bool HasAccessorsImpl(JSObject* holder, 3041 FixedArrayBase* backing_store) { 3042 FixedArray* parameter_map = FixedArray::cast(backing_store); 3043 FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); 3044 return ArgumentsAccessor::HasAccessorsImpl(holder, arguments); 3045 } 3046 3047 static uint32_t GetIndexForEntryImpl(FixedArrayBase* parameters, 3048 uint32_t entry) { 3049 FixedArray* parameter_map = FixedArray::cast(parameters); 3050 uint32_t length = parameter_map->length() - 2; 3051 if (entry < length) return entry; 3052 3053 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 3054 return ArgumentsAccessor::GetIndexForEntryImpl(arguments, entry - length); 3055 } 3056 3057 static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder, 3058 FixedArrayBase* parameters, 3059 uint32_t index, PropertyFilter filter) { 3060 FixedArray* parameter_map = FixedArray::cast(parameters); 3061 if (HasParameterMapArg(parameter_map, index)) return index; 3062 3063 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 3064 uint32_t entry = ArgumentsAccessor::GetEntryForIndexImpl( 3065 isolate, holder, arguments, index, filter); 3066 if (entry == kMaxUInt32) return kMaxUInt32; 3067 return (parameter_map->length() - 2) + entry; 3068 } 3069 3070 static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) { 3071 FixedArray* parameter_map = FixedArray::cast(holder->elements()); 3072 uint32_t length = parameter_map->length() - 2; 3073 if (entry < length) { 3074 return PropertyDetails(NONE, DATA, 0, PropertyCellType::kNoCell); 3075 } 3076 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 3077 return ArgumentsAccessor::GetDetailsImpl(arguments, entry - length); 3078 } 3079 3080 static bool HasParameterMapArg(FixedArray* parameter_map, uint32_t index) { 3081 uint32_t length = parameter_map->length() - 2; 3082 if (index >= length) return false; 3083 return !parameter_map->get(index + 2)->IsTheHole( 3084 parameter_map->GetIsolate()); 3085 } 3086 3087 static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) { 3088 FixedArray* parameter_map = FixedArray::cast(obj->elements()); 3089 uint32_t length = static_cast<uint32_t>(parameter_map->length()) - 2; 3090 if (entry < length) { 3091 // TODO(kmillikin): We could check if this was the last aliased 3092 // parameter, and revert to normal elements in that case. That 3093 // would enable GC of the context. 3094 parameter_map->set_the_hole(entry + 2); 3095 } else { 3096 Subclass::DeleteFromArguments(obj, entry - length); 3097 } 3098 } 3099 3100 static void CollectElementIndicesImpl(Handle<JSObject> object, 3101 Handle<FixedArrayBase> backing_store, 3102 KeyAccumulator* keys) { 3103 Isolate* isolate = keys->isolate(); 3104 uint32_t nof_indices = 0; 3105 Handle<FixedArray> indices = isolate->factory()->NewFixedArray( 3106 GetCapacityImpl(*object, *backing_store)); 3107 DirectCollectElementIndicesImpl(isolate, object, backing_store, 3108 GetKeysConversion::kKeepNumbers, 3109 ENUMERABLE_STRINGS, indices, &nof_indices); 3110 SortIndices(indices, nof_indices); 3111 for (uint32_t i = 0; i < nof_indices; i++) { 3112 keys->AddKey(indices->get(i)); 3113 } 3114 } 3115 3116 static Handle<FixedArray> DirectCollectElementIndicesImpl( 3117 Isolate* isolate, Handle<JSObject> object, 3118 Handle<FixedArrayBase> backing_store, GetKeysConversion convert, 3119 PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices, 3120 uint32_t insertion_index = 0) { 3121 Handle<FixedArray> parameter_map(FixedArray::cast(*backing_store), isolate); 3122 uint32_t length = parameter_map->length() - 2; 3123 3124 for (uint32_t i = 0; i < length; ++i) { 3125 if (parameter_map->get(i + 2)->IsTheHole(isolate)) continue; 3126 if (convert == GetKeysConversion::kConvertToString) { 3127 Handle<String> index_string = isolate->factory()->Uint32ToString(i); 3128 list->set(insertion_index, *index_string); 3129 } else { 3130 list->set(insertion_index, Smi::FromInt(i), SKIP_WRITE_BARRIER); 3131 } 3132 insertion_index++; 3133 } 3134 3135 Handle<FixedArrayBase> store(FixedArrayBase::cast(parameter_map->get(1))); 3136 return ArgumentsAccessor::DirectCollectElementIndicesImpl( 3137 isolate, object, store, convert, filter, list, nof_indices, 3138 insertion_index); 3139 } 3140 3141 static Maybe<bool> IncludesValueImpl(Isolate* isolate, 3142 Handle<JSObject> object, 3143 Handle<Object> value, 3144 uint32_t start_from, uint32_t length) { 3145 DCHECK(JSObject::PrototypeHasNoElements(isolate, *object)); 3146 Handle<Map> original_map = handle(object->map(), isolate); 3147 Handle<FixedArray> parameter_map(FixedArray::cast(object->elements()), 3148 isolate); 3149 bool search_for_hole = value->IsUndefined(isolate); 3150 3151 for (uint32_t k = start_from; k < length; ++k) { 3152 uint32_t entry = GetEntryForIndexImpl(isolate, *object, *parameter_map, k, 3153 ALL_PROPERTIES); 3154 if (entry == kMaxUInt32) { 3155 if (search_for_hole) return Just(true); 3156 continue; 3157 } 3158 3159 Handle<Object> element_k = GetImpl(*parameter_map, entry); 3160 3161 if (element_k->IsAccessorPair()) { 3162 LookupIterator it(isolate, object, k, LookupIterator::OWN); 3163 DCHECK(it.IsFound()); 3164 DCHECK_EQ(it.state(), LookupIterator::ACCESSOR); 3165 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k, 3166 Object::GetPropertyWithAccessor(&it), 3167 Nothing<bool>()); 3168 3169 if (value->SameValueZero(*element_k)) return Just(true); 3170 3171 if (object->map() != *original_map) { 3172 // Some mutation occurred in accessor. Abort "fast" path 3173 return IncludesValueSlowPath(isolate, object, value, k + 1, length); 3174 } 3175 } else if (value->SameValueZero(*element_k)) { 3176 return Just(true); 3177 } 3178 } 3179 return Just(false); 3180 } 3181 3182 static Maybe<int64_t> IndexOfValueImpl(Isolate* isolate, 3183 Handle<JSObject> object, 3184 Handle<Object> value, 3185 uint32_t start_from, uint32_t length) { 3186 DCHECK(JSObject::PrototypeHasNoElements(isolate, *object)); 3187 Handle<Map> original_map = handle(object->map(), isolate); 3188 Handle<FixedArray> parameter_map(FixedArray::cast(object->elements()), 3189 isolate); 3190 3191 for (uint32_t k = start_from; k < length; ++k) { 3192 uint32_t entry = GetEntryForIndexImpl(isolate, *object, *parameter_map, k, 3193 ALL_PROPERTIES); 3194 if (entry == kMaxUInt32) { 3195 continue; 3196 } 3197 3198 Handle<Object> element_k = GetImpl(*parameter_map, entry); 3199 3200 if (element_k->IsAccessorPair()) { 3201 LookupIterator it(isolate, object, k, LookupIterator::OWN); 3202 DCHECK(it.IsFound()); 3203 DCHECK_EQ(it.state(), LookupIterator::ACCESSOR); 3204 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k, 3205 Object::GetPropertyWithAccessor(&it), 3206 Nothing<int64_t>()); 3207 3208 if (value->StrictEquals(*element_k)) { 3209 return Just<int64_t>(k); 3210 } 3211 3212 if (object->map() != *original_map) { 3213 // Some mutation occurred in accessor. Abort "fast" path. 3214 return IndexOfValueSlowPath(isolate, object, value, k + 1, length); 3215 } 3216 } else if (value->StrictEquals(*element_k)) { 3217 return Just<int64_t>(k); 3218 } 3219 } 3220 return Just<int64_t>(-1); 3221 } 3222 }; 3223 3224 3225 class SlowSloppyArgumentsElementsAccessor 3226 : public SloppyArgumentsElementsAccessor< 3227 SlowSloppyArgumentsElementsAccessor, DictionaryElementsAccessor, 3228 ElementsKindTraits<SLOW_SLOPPY_ARGUMENTS_ELEMENTS> > { 3229 public: 3230 explicit SlowSloppyArgumentsElementsAccessor(const char* name) 3231 : SloppyArgumentsElementsAccessor< 3232 SlowSloppyArgumentsElementsAccessor, DictionaryElementsAccessor, 3233 ElementsKindTraits<SLOW_SLOPPY_ARGUMENTS_ELEMENTS> >(name) {} 3234 3235 static void DeleteFromArguments(Handle<JSObject> obj, uint32_t entry) { 3236 Handle<FixedArray> parameter_map(FixedArray::cast(obj->elements())); 3237 Handle<SeededNumberDictionary> dict( 3238 SeededNumberDictionary::cast(parameter_map->get(1))); 3239 // TODO(verwaest): Remove reliance on index in Shrink. 3240 uint32_t index = GetIndexForEntryImpl(*dict, entry); 3241 Handle<Object> result = SeededNumberDictionary::DeleteProperty(dict, entry); 3242 USE(result); 3243 DCHECK(result->IsTrue(dict->GetIsolate())); 3244 Handle<FixedArray> new_elements = 3245 SeededNumberDictionary::Shrink(dict, index); 3246 parameter_map->set(1, *new_elements); 3247 } 3248 3249 static void AddImpl(Handle<JSObject> object, uint32_t index, 3250 Handle<Object> value, PropertyAttributes attributes, 3251 uint32_t new_capacity) { 3252 Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); 3253 Handle<FixedArrayBase> old_elements( 3254 FixedArrayBase::cast(parameter_map->get(1))); 3255 Handle<SeededNumberDictionary> dictionary = 3256 old_elements->IsSeededNumberDictionary() 3257 ? Handle<SeededNumberDictionary>::cast(old_elements) 3258 : JSObject::NormalizeElements(object); 3259 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); 3260 Handle<SeededNumberDictionary> new_dictionary = 3261 SeededNumberDictionary::AddNumberEntry( 3262 dictionary, index, value, details, 3263 object->map()->is_prototype_map()); 3264 if (attributes != NONE) object->RequireSlowElements(*new_dictionary); 3265 if (*dictionary != *new_dictionary) { 3266 FixedArray::cast(object->elements())->set(1, *new_dictionary); 3267 } 3268 } 3269 3270 static void ReconfigureImpl(Handle<JSObject> object, 3271 Handle<FixedArrayBase> store, uint32_t entry, 3272 Handle<Object> value, 3273 PropertyAttributes attributes) { 3274 Handle<FixedArray> parameter_map = Handle<FixedArray>::cast(store); 3275 uint32_t length = parameter_map->length() - 2; 3276 Isolate* isolate = store->GetIsolate(); 3277 if (entry < length) { 3278 Object* probe = parameter_map->get(entry + 2); 3279 DCHECK(!probe->IsTheHole(isolate)); 3280 Context* context = Context::cast(parameter_map->get(0)); 3281 int context_entry = Smi::cast(probe)->value(); 3282 DCHECK(!context->get(context_entry)->IsTheHole(isolate)); 3283 context->set(context_entry, *value); 3284 3285 // Redefining attributes of an aliased element destroys fast aliasing. 3286 parameter_map->set_the_hole(entry + 2); 3287 // For elements that are still writable we re-establish slow aliasing. 3288 if ((attributes & READ_ONLY) == 0) { 3289 value = isolate->factory()->NewAliasedArgumentsEntry(context_entry); 3290 } 3291 3292 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); 3293 Handle<SeededNumberDictionary> arguments( 3294 SeededNumberDictionary::cast(parameter_map->get(1)), isolate); 3295 arguments = SeededNumberDictionary::AddNumberEntry( 3296 arguments, entry, value, details, object->map()->is_prototype_map()); 3297 // If the attributes were NONE, we would have called set rather than 3298 // reconfigure. 3299 DCHECK_NE(NONE, attributes); 3300 object->RequireSlowElements(*arguments); 3301 parameter_map->set(1, *arguments); 3302 } else { 3303 Handle<FixedArrayBase> arguments( 3304 FixedArrayBase::cast(parameter_map->get(1)), isolate); 3305 DictionaryElementsAccessor::ReconfigureImpl( 3306 object, arguments, entry - length, value, attributes); 3307 } 3308 } 3309 }; 3310 3311 3312 class FastSloppyArgumentsElementsAccessor 3313 : public SloppyArgumentsElementsAccessor< 3314 FastSloppyArgumentsElementsAccessor, FastHoleyObjectElementsAccessor, 3315 ElementsKindTraits<FAST_SLOPPY_ARGUMENTS_ELEMENTS> > { 3316 public: 3317 explicit FastSloppyArgumentsElementsAccessor(const char* name) 3318 : SloppyArgumentsElementsAccessor< 3319 FastSloppyArgumentsElementsAccessor, 3320 FastHoleyObjectElementsAccessor, 3321 ElementsKindTraits<FAST_SLOPPY_ARGUMENTS_ELEMENTS> >(name) {} 3322 3323 static Handle<FixedArray> GetArguments(Isolate* isolate, 3324 FixedArrayBase* backing_store) { 3325 FixedArray* parameter_map = FixedArray::cast(backing_store); 3326 return Handle<FixedArray>(FixedArray::cast(parameter_map->get(1)), isolate); 3327 } 3328 3329 static Handle<JSArray> SliceImpl(Handle<JSObject> receiver, uint32_t start, 3330 uint32_t end) { 3331 Isolate* isolate = receiver->GetIsolate(); 3332 uint32_t result_len = end < start ? 0u : end - start; 3333 Handle<JSArray> result_array = isolate->factory()->NewJSArray( 3334 FAST_HOLEY_ELEMENTS, result_len, result_len); 3335 DisallowHeapAllocation no_gc; 3336 FixedArray* elements = FixedArray::cast(result_array->elements()); 3337 FixedArray* parameters = FixedArray::cast(receiver->elements()); 3338 uint32_t insertion_index = 0; 3339 for (uint32_t i = start; i < end; i++) { 3340 uint32_t entry = GetEntryForIndexImpl(isolate, *receiver, parameters, i, 3341 ALL_PROPERTIES); 3342 if (entry != kMaxUInt32 && HasEntryImpl(isolate, parameters, entry)) { 3343 elements->set(insertion_index, *GetImpl(parameters, entry)); 3344 } else { 3345 elements->set_the_hole(insertion_index); 3346 } 3347 insertion_index++; 3348 } 3349 return result_array; 3350 } 3351 3352 static Handle<SeededNumberDictionary> NormalizeImpl( 3353 Handle<JSObject> object, Handle<FixedArrayBase> elements) { 3354 Handle<FixedArray> arguments = 3355 GetArguments(elements->GetIsolate(), *elements); 3356 return FastHoleyObjectElementsAccessor::NormalizeImpl(object, arguments); 3357 } 3358 3359 static void DeleteFromArguments(Handle<JSObject> obj, uint32_t entry) { 3360 Handle<FixedArray> arguments = 3361 GetArguments(obj->GetIsolate(), obj->elements()); 3362 FastHoleyObjectElementsAccessor::DeleteCommon(obj, entry, arguments); 3363 } 3364 3365 static void AddImpl(Handle<JSObject> object, uint32_t index, 3366 Handle<Object> value, PropertyAttributes attributes, 3367 uint32_t new_capacity) { 3368 DCHECK_EQ(NONE, attributes); 3369 Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); 3370 Handle<FixedArrayBase> old_elements( 3371 FixedArrayBase::cast(parameter_map->get(1))); 3372 if (old_elements->IsSeededNumberDictionary() || 3373 static_cast<uint32_t>(old_elements->length()) < new_capacity) { 3374 GrowCapacityAndConvertImpl(object, new_capacity); 3375 } 3376 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 3377 // For fast holey objects, the entry equals the index. The code above made 3378 // sure that there's enough space to store the value. We cannot convert 3379 // index to entry explicitly since the slot still contains the hole, so the 3380 // current EntryForIndex would indicate that it is "absent" by returning 3381 // kMaxUInt32. 3382 FastHoleyObjectElementsAccessor::SetImpl(arguments, index, *value); 3383 } 3384 3385 static void ReconfigureImpl(Handle<JSObject> object, 3386 Handle<FixedArrayBase> store, uint32_t entry, 3387 Handle<Object> value, 3388 PropertyAttributes attributes) { 3389 Handle<SeededNumberDictionary> dictionary = 3390 JSObject::NormalizeElements(object); 3391 FixedArray::cast(*store)->set(1, *dictionary); 3392 uint32_t length = static_cast<uint32_t>(store->length()) - 2; 3393 if (entry >= length) { 3394 entry = dictionary->FindEntry(entry - length) + length; 3395 } 3396 SlowSloppyArgumentsElementsAccessor::ReconfigureImpl(object, store, entry, 3397 value, attributes); 3398 } 3399 3400 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 3401 FixedArrayBase* to, ElementsKind from_kind, 3402 uint32_t to_start, int packed_size, 3403 int copy_size) { 3404 DCHECK(!to->IsDictionary()); 3405 if (from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS) { 3406 CopyDictionaryToObjectElements(from, from_start, to, FAST_HOLEY_ELEMENTS, 3407 to_start, copy_size); 3408 } else { 3409 DCHECK_EQ(FAST_SLOPPY_ARGUMENTS_ELEMENTS, from_kind); 3410 CopyObjectToObjectElements(from, FAST_HOLEY_ELEMENTS, from_start, to, 3411 FAST_HOLEY_ELEMENTS, to_start, copy_size); 3412 } 3413 } 3414 3415 static void GrowCapacityAndConvertImpl(Handle<JSObject> object, 3416 uint32_t capacity) { 3417 Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); 3418 Handle<FixedArray> old_elements(FixedArray::cast(parameter_map->get(1))); 3419 ElementsKind from_kind = object->GetElementsKind(); 3420 // This method should only be called if there's a reason to update the 3421 // elements. 3422 DCHECK(from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS || 3423 static_cast<uint32_t>(old_elements->length()) < capacity); 3424 Handle<FixedArrayBase> elements = 3425 ConvertElementsWithCapacity(object, old_elements, from_kind, capacity); 3426 Handle<Map> new_map = JSObject::GetElementsTransitionMap( 3427 object, FAST_SLOPPY_ARGUMENTS_ELEMENTS); 3428 JSObject::MigrateToMap(object, new_map); 3429 parameter_map->set(1, *elements); 3430 JSObject::ValidateElements(object); 3431 } 3432 }; 3433 3434 template <typename Subclass, typename BackingStoreAccessor, typename KindTraits> 3435 class StringWrapperElementsAccessor 3436 : public ElementsAccessorBase<Subclass, KindTraits> { 3437 public: 3438 explicit StringWrapperElementsAccessor(const char* name) 3439 : ElementsAccessorBase<Subclass, KindTraits>(name) { 3440 USE(KindTraits::Kind); 3441 } 3442 3443 static Handle<Object> GetImpl(Handle<JSObject> holder, uint32_t entry) { 3444 Isolate* isolate = holder->GetIsolate(); 3445 Handle<String> string(GetString(*holder), isolate); 3446 uint32_t length = static_cast<uint32_t>(string->length()); 3447 if (entry < length) { 3448 return isolate->factory()->LookupSingleCharacterStringFromCode( 3449 String::Flatten(string)->Get(entry)); 3450 } 3451 return BackingStoreAccessor::GetImpl(holder, entry - length); 3452 } 3453 3454 static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) { 3455 uint32_t length = static_cast<uint32_t>(GetString(holder)->length()); 3456 if (entry < length) { 3457 PropertyAttributes attributes = 3458 static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); 3459 return PropertyDetails(attributes, v8::internal::DATA, 0, 3460 PropertyCellType::kNoCell); 3461 } 3462 return BackingStoreAccessor::GetDetailsImpl(holder, entry - length); 3463 } 3464 3465 static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject* holder, 3466 FixedArrayBase* backing_store, 3467 uint32_t index, PropertyFilter filter) { 3468 uint32_t length = static_cast<uint32_t>(GetString(holder)->length()); 3469 if (index < length) return index; 3470 uint32_t backing_store_entry = BackingStoreAccessor::GetEntryForIndexImpl( 3471 isolate, holder, backing_store, index, filter); 3472 if (backing_store_entry == kMaxUInt32) return kMaxUInt32; 3473 DCHECK(backing_store_entry < kMaxUInt32 - length); 3474 return backing_store_entry + length; 3475 } 3476 3477 static void DeleteImpl(Handle<JSObject> holder, uint32_t entry) { 3478 uint32_t length = static_cast<uint32_t>(GetString(*holder)->length()); 3479 if (entry < length) { 3480 return; // String contents can't be deleted. 3481 } 3482 BackingStoreAccessor::DeleteImpl(holder, entry - length); 3483 } 3484 3485 static void SetImpl(Handle<JSObject> holder, uint32_t entry, Object* value) { 3486 uint32_t length = static_cast<uint32_t>(GetString(*holder)->length()); 3487 if (entry < length) { 3488 return; // String contents are read-only. 3489 } 3490 BackingStoreAccessor::SetImpl(holder->elements(), entry - length, value); 3491 } 3492 3493 static void AddImpl(Handle<JSObject> object, uint32_t index, 3494 Handle<Object> value, PropertyAttributes attributes, 3495 uint32_t new_capacity) { 3496 DCHECK(index >= static_cast<uint32_t>(GetString(*object)->length())); 3497 // Explicitly grow fast backing stores if needed. Dictionaries know how to 3498 // extend their capacity themselves. 3499 if (KindTraits::Kind == FAST_STRING_WRAPPER_ELEMENTS && 3500 (object->GetElementsKind() == SLOW_STRING_WRAPPER_ELEMENTS || 3501 BackingStoreAccessor::GetCapacityImpl(*object, object->elements()) != 3502 new_capacity)) { 3503 GrowCapacityAndConvertImpl(object, new_capacity); 3504 } 3505 BackingStoreAccessor::AddImpl(object, index, value, attributes, 3506 new_capacity); 3507 } 3508 3509 static void ReconfigureImpl(Handle<JSObject> object, 3510 Handle<FixedArrayBase> store, uint32_t entry, 3511 Handle<Object> value, 3512 PropertyAttributes attributes) { 3513 uint32_t length = static_cast<uint32_t>(GetString(*object)->length()); 3514 if (entry < length) { 3515 return; // String contents can't be reconfigured. 3516 } 3517 BackingStoreAccessor::ReconfigureImpl(object, store, entry - length, value, 3518 attributes); 3519 } 3520 3521 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver, 3522 KeyAccumulator* accumulator, 3523 AddKeyConversion convert) { 3524 Isolate* isolate = receiver->GetIsolate(); 3525 Handle<String> string(GetString(*receiver), isolate); 3526 string = String::Flatten(string); 3527 uint32_t length = static_cast<uint32_t>(string->length()); 3528 for (uint32_t i = 0; i < length; i++) { 3529 accumulator->AddKey( 3530 isolate->factory()->LookupSingleCharacterStringFromCode( 3531 string->Get(i)), 3532 convert); 3533 } 3534 BackingStoreAccessor::AddElementsToKeyAccumulatorImpl(receiver, accumulator, 3535 convert); 3536 } 3537 3538 static void CollectElementIndicesImpl(Handle<JSObject> object, 3539 Handle<FixedArrayBase> backing_store, 3540 KeyAccumulator* keys) { 3541 uint32_t length = GetString(*object)->length(); 3542 Factory* factory = keys->isolate()->factory(); 3543 for (uint32_t i = 0; i < length; i++) { 3544 keys->AddKey(factory->NewNumberFromUint(i)); 3545 } 3546 BackingStoreAccessor::CollectElementIndicesImpl(object, backing_store, 3547 keys); 3548 } 3549 3550 static void GrowCapacityAndConvertImpl(Handle<JSObject> object, 3551 uint32_t capacity) { 3552 Handle<FixedArrayBase> old_elements(object->elements()); 3553 ElementsKind from_kind = object->GetElementsKind(); 3554 // This method should only be called if there's a reason to update the 3555 // elements. 3556 DCHECK(from_kind == SLOW_STRING_WRAPPER_ELEMENTS || 3557 static_cast<uint32_t>(old_elements->length()) < capacity); 3558 Subclass::BasicGrowCapacityAndConvertImpl(object, old_elements, from_kind, 3559 FAST_STRING_WRAPPER_ELEMENTS, 3560 capacity); 3561 } 3562 3563 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 3564 FixedArrayBase* to, ElementsKind from_kind, 3565 uint32_t to_start, int packed_size, 3566 int copy_size) { 3567 DCHECK(!to->IsDictionary()); 3568 if (from_kind == SLOW_STRING_WRAPPER_ELEMENTS) { 3569 CopyDictionaryToObjectElements(from, from_start, to, FAST_HOLEY_ELEMENTS, 3570 to_start, copy_size); 3571 } else { 3572 DCHECK_EQ(FAST_STRING_WRAPPER_ELEMENTS, from_kind); 3573 CopyObjectToObjectElements(from, FAST_HOLEY_ELEMENTS, from_start, to, 3574 FAST_HOLEY_ELEMENTS, to_start, copy_size); 3575 } 3576 } 3577 3578 static uint32_t NumberOfElementsImpl(JSObject* object, 3579 FixedArrayBase* backing_store) { 3580 uint32_t length = GetString(object)->length(); 3581 return length + 3582 BackingStoreAccessor::NumberOfElementsImpl(object, backing_store); 3583 } 3584 3585 private: 3586 static String* GetString(JSObject* holder) { 3587 DCHECK(holder->IsJSValue()); 3588 JSValue* js_value = JSValue::cast(holder); 3589 DCHECK(js_value->value()->IsString()); 3590 return String::cast(js_value->value()); 3591 } 3592 }; 3593 3594 class FastStringWrapperElementsAccessor 3595 : public StringWrapperElementsAccessor< 3596 FastStringWrapperElementsAccessor, FastHoleyObjectElementsAccessor, 3597 ElementsKindTraits<FAST_STRING_WRAPPER_ELEMENTS>> { 3598 public: 3599 explicit FastStringWrapperElementsAccessor(const char* name) 3600 : StringWrapperElementsAccessor< 3601 FastStringWrapperElementsAccessor, FastHoleyObjectElementsAccessor, 3602 ElementsKindTraits<FAST_STRING_WRAPPER_ELEMENTS>>(name) {} 3603 3604 static Handle<SeededNumberDictionary> NormalizeImpl( 3605 Handle<JSObject> object, Handle<FixedArrayBase> elements) { 3606 return FastHoleyObjectElementsAccessor::NormalizeImpl(object, elements); 3607 } 3608 }; 3609 3610 class SlowStringWrapperElementsAccessor 3611 : public StringWrapperElementsAccessor< 3612 SlowStringWrapperElementsAccessor, DictionaryElementsAccessor, 3613 ElementsKindTraits<SLOW_STRING_WRAPPER_ELEMENTS>> { 3614 public: 3615 explicit SlowStringWrapperElementsAccessor(const char* name) 3616 : StringWrapperElementsAccessor< 3617 SlowStringWrapperElementsAccessor, DictionaryElementsAccessor, 3618 ElementsKindTraits<SLOW_STRING_WRAPPER_ELEMENTS>>(name) {} 3619 3620 static bool HasAccessorsImpl(JSObject* holder, 3621 FixedArrayBase* backing_store) { 3622 return DictionaryElementsAccessor::HasAccessorsImpl(holder, backing_store); 3623 } 3624 }; 3625 3626 } // namespace 3627 3628 3629 void CheckArrayAbuse(Handle<JSObject> obj, const char* op, uint32_t index, 3630 bool allow_appending) { 3631 DisallowHeapAllocation no_allocation; 3632 Object* raw_length = NULL; 3633 const char* elements_type = "array"; 3634 if (obj->IsJSArray()) { 3635 JSArray* array = JSArray::cast(*obj); 3636 raw_length = array->length(); 3637 } else { 3638 raw_length = Smi::FromInt(obj->elements()->length()); 3639 elements_type = "object"; 3640 } 3641 3642 if (raw_length->IsNumber()) { 3643 double n = raw_length->Number(); 3644 if (FastI2D(FastD2UI(n)) == n) { 3645 int32_t int32_length = DoubleToInt32(n); 3646 uint32_t compare_length = static_cast<uint32_t>(int32_length); 3647 if (allow_appending) compare_length++; 3648 if (index >= compare_length) { 3649 PrintF("[OOB %s %s (%s length = %d, element accessed = %d) in ", 3650 elements_type, op, elements_type, static_cast<int>(int32_length), 3651 static_cast<int>(index)); 3652 TraceTopFrame(obj->GetIsolate()); 3653 PrintF("]\n"); 3654 } 3655 } else { 3656 PrintF("[%s elements length not integer value in ", elements_type); 3657 TraceTopFrame(obj->GetIsolate()); 3658 PrintF("]\n"); 3659 } 3660 } else { 3661 PrintF("[%s elements length not a number in ", elements_type); 3662 TraceTopFrame(obj->GetIsolate()); 3663 PrintF("]\n"); 3664 } 3665 } 3666 3667 3668 MaybeHandle<Object> ArrayConstructInitializeElements(Handle<JSArray> array, 3669 Arguments* args) { 3670 if (args->length() == 0) { 3671 // Optimize the case where there are no parameters passed. 3672 JSArray::Initialize(array, JSArray::kPreallocatedArrayElements); 3673 return array; 3674 3675 } else if (args->length() == 1 && args->at<Object>(0)->IsNumber()) { 3676 uint32_t length; 3677 if (!args->at<Object>(0)->ToArrayLength(&length)) { 3678 return ThrowArrayLengthRangeError(array->GetIsolate()); 3679 } 3680 3681 // Optimize the case where there is one argument and the argument is a small 3682 // smi. 3683 if (length > 0 && length < JSArray::kInitialMaxFastElementArray) { 3684 ElementsKind elements_kind = array->GetElementsKind(); 3685 JSArray::Initialize(array, length, length); 3686 3687 if (!IsFastHoleyElementsKind(elements_kind)) { 3688 elements_kind = GetHoleyElementsKind(elements_kind); 3689 JSObject::TransitionElementsKind(array, elements_kind); 3690 } 3691 } else if (length == 0) { 3692 JSArray::Initialize(array, JSArray::kPreallocatedArrayElements); 3693 } else { 3694 // Take the argument as the length. 3695 JSArray::Initialize(array, 0); 3696 JSArray::SetLength(array, length); 3697 } 3698 return array; 3699 } 3700 3701 Factory* factory = array->GetIsolate()->factory(); 3702 3703 // Set length and elements on the array. 3704 int number_of_elements = args->length(); 3705 JSObject::EnsureCanContainElements( 3706 array, args, 0, number_of_elements, ALLOW_CONVERTED_DOUBLE_ELEMENTS); 3707 3708 // Allocate an appropriately typed elements array. 3709 ElementsKind elements_kind = array->GetElementsKind(); 3710 Handle<FixedArrayBase> elms; 3711 if (IsFastDoubleElementsKind(elements_kind)) { 3712 elms = Handle<FixedArrayBase>::cast( 3713 factory->NewFixedDoubleArray(number_of_elements)); 3714 } else { 3715 elms = Handle<FixedArrayBase>::cast( 3716 factory->NewFixedArrayWithHoles(number_of_elements)); 3717 } 3718 3719 // Fill in the content 3720 switch (elements_kind) { 3721 case FAST_HOLEY_SMI_ELEMENTS: 3722 case FAST_SMI_ELEMENTS: { 3723 Handle<FixedArray> smi_elms = Handle<FixedArray>::cast(elms); 3724 for (int entry = 0; entry < number_of_elements; entry++) { 3725 smi_elms->set(entry, (*args)[entry], SKIP_WRITE_BARRIER); 3726 } 3727 break; 3728 } 3729 case FAST_HOLEY_ELEMENTS: 3730 case FAST_ELEMENTS: { 3731 DisallowHeapAllocation no_gc; 3732 WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); 3733 Handle<FixedArray> object_elms = Handle<FixedArray>::cast(elms); 3734 for (int entry = 0; entry < number_of_elements; entry++) { 3735 object_elms->set(entry, (*args)[entry], mode); 3736 } 3737 break; 3738 } 3739 case FAST_HOLEY_DOUBLE_ELEMENTS: 3740 case FAST_DOUBLE_ELEMENTS: { 3741 Handle<FixedDoubleArray> double_elms = 3742 Handle<FixedDoubleArray>::cast(elms); 3743 for (int entry = 0; entry < number_of_elements; entry++) { 3744 double_elms->set(entry, (*args)[entry]->Number()); 3745 } 3746 break; 3747 } 3748 default: 3749 UNREACHABLE(); 3750 break; 3751 } 3752 3753 array->set_elements(*elms); 3754 array->set_length(Smi::FromInt(number_of_elements)); 3755 return array; 3756 } 3757 3758 3759 void ElementsAccessor::InitializeOncePerProcess() { 3760 static ElementsAccessor* accessor_array[] = { 3761 #define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind), 3762 ELEMENTS_LIST(ACCESSOR_ARRAY) 3763 #undef ACCESSOR_ARRAY 3764 }; 3765 3766 STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) == 3767 kElementsKindCount); 3768 3769 elements_accessors_ = accessor_array; 3770 } 3771 3772 3773 void ElementsAccessor::TearDown() { 3774 if (elements_accessors_ == NULL) return; 3775 #define ACCESSOR_DELETE(Class, Kind, Store) delete elements_accessors_[Kind]; 3776 ELEMENTS_LIST(ACCESSOR_DELETE) 3777 #undef ACCESSOR_DELETE 3778 elements_accessors_ = NULL; 3779 } 3780 3781 Handle<JSArray> ElementsAccessor::Concat(Isolate* isolate, Arguments* args, 3782 uint32_t concat_size, 3783 uint32_t result_len) { 3784 ElementsKind result_elements_kind = GetInitialFastElementsKind(); 3785 bool has_raw_doubles = false; 3786 { 3787 DisallowHeapAllocation no_gc; 3788 bool is_holey = false; 3789 for (uint32_t i = 0; i < concat_size; i++) { 3790 Object* arg = (*args)[i]; 3791 ElementsKind arg_kind = JSArray::cast(arg)->GetElementsKind(); 3792 has_raw_doubles = has_raw_doubles || IsFastDoubleElementsKind(arg_kind); 3793 is_holey = is_holey || IsFastHoleyElementsKind(arg_kind); 3794 result_elements_kind = 3795 GetMoreGeneralElementsKind(result_elements_kind, arg_kind); 3796 } 3797 if (is_holey) { 3798 result_elements_kind = GetHoleyElementsKind(result_elements_kind); 3799 } 3800 } 3801 3802 // If a double array is concatted into a fast elements array, the fast 3803 // elements array needs to be initialized to contain proper holes, since 3804 // boxing doubles may cause incremental marking. 3805 bool requires_double_boxing = 3806 has_raw_doubles && !IsFastDoubleElementsKind(result_elements_kind); 3807 ArrayStorageAllocationMode mode = requires_double_boxing 3808 ? INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE 3809 : DONT_INITIALIZE_ARRAY_ELEMENTS; 3810 Handle<JSArray> result_array = isolate->factory()->NewJSArray( 3811 result_elements_kind, result_len, result_len, mode); 3812 if (result_len == 0) return result_array; 3813 3814 uint32_t insertion_index = 0; 3815 Handle<FixedArrayBase> storage(result_array->elements(), isolate); 3816 ElementsAccessor* accessor = ElementsAccessor::ForKind(result_elements_kind); 3817 for (uint32_t i = 0; i < concat_size; i++) { 3818 // It is crucial to keep |array| in a raw pointer form to avoid 3819 // performance degradation. 3820 JSArray* array = JSArray::cast((*args)[i]); 3821 uint32_t len = 0; 3822 array->length()->ToArrayLength(&len); 3823 if (len == 0) continue; 3824 ElementsKind from_kind = array->GetElementsKind(); 3825 accessor->CopyElements(array, 0, from_kind, storage, insertion_index, len); 3826 insertion_index += len; 3827 } 3828 3829 DCHECK_EQ(insertion_index, result_len); 3830 return result_array; 3831 } 3832 3833 ElementsAccessor** ElementsAccessor::elements_accessors_ = NULL; 3834 } // namespace internal 3835 } // namespace v8 3836