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