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/messages.h" 11 #include "src/objects-inl.h" 12 #include "src/utils.h" 13 14 // Each concrete ElementsAccessor can handle exactly one ElementsKind, 15 // several abstract ElementsAccessor classes are used to allow sharing 16 // common code. 17 // 18 // Inheritance hierarchy: 19 // - ElementsAccessorBase (abstract) 20 // - FastElementsAccessor (abstract) 21 // - FastSmiOrObjectElementsAccessor 22 // - FastPackedSmiElementsAccessor 23 // - FastHoleySmiElementsAccessor 24 // - FastPackedObjectElementsAccessor 25 // - FastHoleyObjectElementsAccessor 26 // - FastDoubleElementsAccessor 27 // - FastPackedDoubleElementsAccessor 28 // - FastHoleyDoubleElementsAccessor 29 // - TypedElementsAccessor: template, with instantiations: 30 // - FixedUint8ElementsAccessor 31 // - FixedInt8ElementsAccessor 32 // - FixedUint16ElementsAccessor 33 // - FixedInt16ElementsAccessor 34 // - FixedUint32ElementsAccessor 35 // - FixedInt32ElementsAccessor 36 // - FixedFloat32ElementsAccessor 37 // - FixedFloat64ElementsAccessor 38 // - FixedUint8ClampedElementsAccessor 39 // - DictionaryElementsAccessor 40 // - SloppyArgumentsElementsAccessor 41 // - FastSloppyArgumentsElementsAccessor 42 // - SlowSloppyArgumentsElementsAccessor 43 44 45 namespace v8 { 46 namespace internal { 47 48 49 namespace { 50 51 52 static const int kPackedSizeNotKnown = -1; 53 54 enum Where { AT_START, AT_END }; 55 56 57 // First argument in list is the accessor class, the second argument is the 58 // accessor ElementsKind, and the third is the backing store class. Use the 59 // fast element handler for smi-only arrays. The implementation is currently 60 // identical. Note that the order must match that of the ElementsKind enum for 61 // the |accessor_array[]| below to work. 62 #define ELEMENTS_LIST(V) \ 63 V(FastPackedSmiElementsAccessor, FAST_SMI_ELEMENTS, FixedArray) \ 64 V(FastHoleySmiElementsAccessor, FAST_HOLEY_SMI_ELEMENTS, FixedArray) \ 65 V(FastPackedObjectElementsAccessor, FAST_ELEMENTS, FixedArray) \ 66 V(FastHoleyObjectElementsAccessor, FAST_HOLEY_ELEMENTS, FixedArray) \ 67 V(FastPackedDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS, FixedDoubleArray) \ 68 V(FastHoleyDoubleElementsAccessor, FAST_HOLEY_DOUBLE_ELEMENTS, \ 69 FixedDoubleArray) \ 70 V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, SeededNumberDictionary) \ 71 V(FastSloppyArgumentsElementsAccessor, FAST_SLOPPY_ARGUMENTS_ELEMENTS, \ 72 FixedArray) \ 73 V(SlowSloppyArgumentsElementsAccessor, SLOW_SLOPPY_ARGUMENTS_ELEMENTS, \ 74 FixedArray) \ 75 V(FixedUint8ElementsAccessor, UINT8_ELEMENTS, FixedUint8Array) \ 76 V(FixedInt8ElementsAccessor, INT8_ELEMENTS, FixedInt8Array) \ 77 V(FixedUint16ElementsAccessor, UINT16_ELEMENTS, FixedUint16Array) \ 78 V(FixedInt16ElementsAccessor, INT16_ELEMENTS, FixedInt16Array) \ 79 V(FixedUint32ElementsAccessor, UINT32_ELEMENTS, FixedUint32Array) \ 80 V(FixedInt32ElementsAccessor, INT32_ELEMENTS, FixedInt32Array) \ 81 V(FixedFloat32ElementsAccessor, FLOAT32_ELEMENTS, FixedFloat32Array) \ 82 V(FixedFloat64ElementsAccessor, FLOAT64_ELEMENTS, FixedFloat64Array) \ 83 V(FixedUint8ClampedElementsAccessor, UINT8_CLAMPED_ELEMENTS, \ 84 FixedUint8ClampedArray) 85 86 87 template<ElementsKind Kind> class ElementsKindTraits { 88 public: 89 typedef FixedArrayBase BackingStore; 90 }; 91 92 #define ELEMENTS_TRAITS(Class, KindParam, Store) \ 93 template<> class ElementsKindTraits<KindParam> { \ 94 public: /* NOLINT */ \ 95 static const ElementsKind Kind = KindParam; \ 96 typedef Store BackingStore; \ 97 }; 98 ELEMENTS_LIST(ELEMENTS_TRAITS) 99 #undef ELEMENTS_TRAITS 100 101 102 MUST_USE_RESULT 103 MaybeHandle<Object> ThrowArrayLengthRangeError(Isolate* isolate) { 104 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidArrayLength), 105 Object); 106 } 107 108 109 void CopyObjectToObjectElements(FixedArrayBase* from_base, 110 ElementsKind from_kind, uint32_t from_start, 111 FixedArrayBase* to_base, ElementsKind to_kind, 112 uint32_t to_start, int raw_copy_size) { 113 DCHECK(to_base->map() != 114 from_base->GetIsolate()->heap()->fixed_cow_array_map()); 115 DisallowHeapAllocation no_allocation; 116 int copy_size = raw_copy_size; 117 if (raw_copy_size < 0) { 118 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 119 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 120 copy_size = Min(from_base->length() - from_start, 121 to_base->length() - to_start); 122 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 123 int start = to_start + copy_size; 124 int length = to_base->length() - start; 125 if (length > 0) { 126 Heap* heap = from_base->GetHeap(); 127 MemsetPointer(FixedArray::cast(to_base)->data_start() + start, 128 heap->the_hole_value(), length); 129 } 130 } 131 } 132 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 133 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 134 if (copy_size == 0) return; 135 FixedArray* from = FixedArray::cast(from_base); 136 FixedArray* to = FixedArray::cast(to_base); 137 DCHECK(IsFastSmiOrObjectElementsKind(from_kind)); 138 DCHECK(IsFastSmiOrObjectElementsKind(to_kind)); 139 140 WriteBarrierMode write_barrier_mode = 141 (IsFastObjectElementsKind(from_kind) && IsFastObjectElementsKind(to_kind)) 142 ? UPDATE_WRITE_BARRIER 143 : SKIP_WRITE_BARRIER; 144 for (int i = 0; i < copy_size; i++) { 145 Object* value = from->get(from_start + i); 146 to->set(to_start + i, value, write_barrier_mode); 147 } 148 } 149 150 151 static void CopyDictionaryToObjectElements( 152 FixedArrayBase* from_base, uint32_t from_start, FixedArrayBase* to_base, 153 ElementsKind to_kind, uint32_t to_start, int raw_copy_size) { 154 DisallowHeapAllocation no_allocation; 155 SeededNumberDictionary* from = SeededNumberDictionary::cast(from_base); 156 int copy_size = raw_copy_size; 157 if (raw_copy_size < 0) { 158 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 159 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 160 copy_size = from->max_number_key() + 1 - from_start; 161 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 162 int start = to_start + copy_size; 163 int length = to_base->length() - start; 164 if (length > 0) { 165 Heap* heap = from->GetHeap(); 166 MemsetPointer(FixedArray::cast(to_base)->data_start() + start, 167 heap->the_hole_value(), length); 168 } 169 } 170 } 171 DCHECK(to_base != from_base); 172 DCHECK(IsFastSmiOrObjectElementsKind(to_kind)); 173 if (copy_size == 0) return; 174 FixedArray* to = FixedArray::cast(to_base); 175 uint32_t to_length = to->length(); 176 if (to_start + copy_size > to_length) { 177 copy_size = to_length - to_start; 178 } 179 WriteBarrierMode write_barrier_mode = IsFastObjectElementsKind(to_kind) 180 ? UPDATE_WRITE_BARRIER 181 : SKIP_WRITE_BARRIER; 182 for (int i = 0; i < copy_size; i++) { 183 int entry = from->FindEntry(i + from_start); 184 if (entry != SeededNumberDictionary::kNotFound) { 185 Object* value = from->ValueAt(entry); 186 DCHECK(!value->IsTheHole()); 187 to->set(i + to_start, value, write_barrier_mode); 188 } else { 189 to->set_the_hole(i + to_start); 190 } 191 } 192 } 193 194 195 // NOTE: this method violates the handlified function signature convention: 196 // raw pointer parameters in the function that allocates. 197 // See ElementsAccessorBase::CopyElements() for details. 198 static void CopyDoubleToObjectElements(FixedArrayBase* from_base, 199 uint32_t from_start, 200 FixedArrayBase* to_base, 201 uint32_t to_start, int raw_copy_size) { 202 int copy_size = raw_copy_size; 203 if (raw_copy_size < 0) { 204 DisallowHeapAllocation no_allocation; 205 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 206 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 207 copy_size = Min(from_base->length() - from_start, 208 to_base->length() - to_start); 209 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 210 // Also initialize the area that will be copied over since HeapNumber 211 // allocation below can cause an incremental marking step, requiring all 212 // existing heap objects to be propertly initialized. 213 int start = to_start; 214 int length = to_base->length() - start; 215 if (length > 0) { 216 Heap* heap = from_base->GetHeap(); 217 MemsetPointer(FixedArray::cast(to_base)->data_start() + start, 218 heap->the_hole_value(), length); 219 } 220 } 221 } 222 223 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 224 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 225 if (copy_size == 0) return; 226 227 // From here on, the code below could actually allocate. Therefore the raw 228 // values are wrapped into handles. 229 Isolate* isolate = from_base->GetIsolate(); 230 Handle<FixedDoubleArray> from(FixedDoubleArray::cast(from_base), isolate); 231 Handle<FixedArray> to(FixedArray::cast(to_base), isolate); 232 233 // create an outer loop to not waste too much time on creating HandleScopes 234 // on the other hand we might overflow a single handle scope depending on 235 // the copy_size 236 int offset = 0; 237 while (offset < copy_size) { 238 HandleScope scope(isolate); 239 offset += 100; 240 for (int i = offset - 100; i < offset && i < copy_size; ++i) { 241 Handle<Object> value = FixedDoubleArray::get(from, i + from_start); 242 to->set(i + to_start, *value, UPDATE_WRITE_BARRIER); 243 } 244 } 245 } 246 247 248 static void CopyDoubleToDoubleElements(FixedArrayBase* from_base, 249 uint32_t from_start, 250 FixedArrayBase* to_base, 251 uint32_t to_start, int raw_copy_size) { 252 DisallowHeapAllocation no_allocation; 253 int copy_size = raw_copy_size; 254 if (raw_copy_size < 0) { 255 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 256 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 257 copy_size = Min(from_base->length() - from_start, 258 to_base->length() - to_start); 259 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 260 for (int i = to_start + copy_size; i < to_base->length(); ++i) { 261 FixedDoubleArray::cast(to_base)->set_the_hole(i); 262 } 263 } 264 } 265 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 266 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 267 if (copy_size == 0) return; 268 FixedDoubleArray* from = FixedDoubleArray::cast(from_base); 269 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 270 Address to_address = to->address() + FixedDoubleArray::kHeaderSize; 271 Address from_address = from->address() + FixedDoubleArray::kHeaderSize; 272 to_address += kDoubleSize * to_start; 273 from_address += kDoubleSize * from_start; 274 int words_per_double = (kDoubleSize / kPointerSize); 275 CopyWords(reinterpret_cast<Object**>(to_address), 276 reinterpret_cast<Object**>(from_address), 277 static_cast<size_t>(words_per_double * copy_size)); 278 } 279 280 281 static void CopySmiToDoubleElements(FixedArrayBase* from_base, 282 uint32_t from_start, 283 FixedArrayBase* to_base, uint32_t to_start, 284 int raw_copy_size) { 285 DisallowHeapAllocation no_allocation; 286 int copy_size = raw_copy_size; 287 if (raw_copy_size < 0) { 288 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 289 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 290 copy_size = from_base->length() - from_start; 291 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 292 for (int i = to_start + copy_size; i < to_base->length(); ++i) { 293 FixedDoubleArray::cast(to_base)->set_the_hole(i); 294 } 295 } 296 } 297 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 298 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 299 if (copy_size == 0) return; 300 FixedArray* from = FixedArray::cast(from_base); 301 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 302 Object* the_hole = from->GetHeap()->the_hole_value(); 303 for (uint32_t from_end = from_start + static_cast<uint32_t>(copy_size); 304 from_start < from_end; from_start++, to_start++) { 305 Object* hole_or_smi = from->get(from_start); 306 if (hole_or_smi == the_hole) { 307 to->set_the_hole(to_start); 308 } else { 309 to->set(to_start, Smi::cast(hole_or_smi)->value()); 310 } 311 } 312 } 313 314 315 static void CopyPackedSmiToDoubleElements(FixedArrayBase* from_base, 316 uint32_t from_start, 317 FixedArrayBase* to_base, 318 uint32_t to_start, int packed_size, 319 int raw_copy_size) { 320 DisallowHeapAllocation no_allocation; 321 int copy_size = raw_copy_size; 322 uint32_t to_end; 323 if (raw_copy_size < 0) { 324 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 325 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 326 copy_size = packed_size - from_start; 327 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 328 to_end = to_base->length(); 329 for (uint32_t i = to_start + copy_size; i < to_end; ++i) { 330 FixedDoubleArray::cast(to_base)->set_the_hole(i); 331 } 332 } else { 333 to_end = to_start + static_cast<uint32_t>(copy_size); 334 } 335 } else { 336 to_end = to_start + static_cast<uint32_t>(copy_size); 337 } 338 DCHECK(static_cast<int>(to_end) <= to_base->length()); 339 DCHECK(packed_size >= 0 && packed_size <= copy_size); 340 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 341 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 342 if (copy_size == 0) return; 343 FixedArray* from = FixedArray::cast(from_base); 344 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 345 for (uint32_t from_end = from_start + static_cast<uint32_t>(packed_size); 346 from_start < from_end; from_start++, to_start++) { 347 Object* smi = from->get(from_start); 348 DCHECK(!smi->IsTheHole()); 349 to->set(to_start, Smi::cast(smi)->value()); 350 } 351 } 352 353 354 static void CopyObjectToDoubleElements(FixedArrayBase* from_base, 355 uint32_t from_start, 356 FixedArrayBase* to_base, 357 uint32_t to_start, int raw_copy_size) { 358 DisallowHeapAllocation no_allocation; 359 int copy_size = raw_copy_size; 360 if (raw_copy_size < 0) { 361 DCHECK(raw_copy_size == ElementsAccessor::kCopyToEnd || 362 raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 363 copy_size = from_base->length() - from_start; 364 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 365 for (int i = to_start + copy_size; i < to_base->length(); ++i) { 366 FixedDoubleArray::cast(to_base)->set_the_hole(i); 367 } 368 } 369 } 370 DCHECK((copy_size + static_cast<int>(to_start)) <= to_base->length() && 371 (copy_size + static_cast<int>(from_start)) <= from_base->length()); 372 if (copy_size == 0) return; 373 FixedArray* from = FixedArray::cast(from_base); 374 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 375 Object* the_hole = from->GetHeap()->the_hole_value(); 376 for (uint32_t from_end = from_start + copy_size; 377 from_start < from_end; from_start++, to_start++) { 378 Object* hole_or_object = from->get(from_start); 379 if (hole_or_object == the_hole) { 380 to->set_the_hole(to_start); 381 } else { 382 to->set(to_start, hole_or_object->Number()); 383 } 384 } 385 } 386 387 388 static void CopyDictionaryToDoubleElements(FixedArrayBase* from_base, 389 uint32_t from_start, 390 FixedArrayBase* to_base, 391 uint32_t to_start, 392 int raw_copy_size) { 393 DisallowHeapAllocation no_allocation; 394 SeededNumberDictionary* from = SeededNumberDictionary::cast(from_base); 395 int copy_size = raw_copy_size; 396 if (copy_size < 0) { 397 DCHECK(copy_size == ElementsAccessor::kCopyToEnd || 398 copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); 399 copy_size = from->max_number_key() + 1 - from_start; 400 if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { 401 for (int i = to_start + copy_size; i < to_base->length(); ++i) { 402 FixedDoubleArray::cast(to_base)->set_the_hole(i); 403 } 404 } 405 } 406 if (copy_size == 0) return; 407 FixedDoubleArray* to = FixedDoubleArray::cast(to_base); 408 uint32_t to_length = to->length(); 409 if (to_start + copy_size > to_length) { 410 copy_size = to_length - to_start; 411 } 412 for (int i = 0; i < copy_size; i++) { 413 int entry = from->FindEntry(i + from_start); 414 if (entry != SeededNumberDictionary::kNotFound) { 415 to->set(i + to_start, from->ValueAt(entry)->Number()); 416 } else { 417 to->set_the_hole(i + to_start); 418 } 419 } 420 } 421 422 423 static void TraceTopFrame(Isolate* isolate) { 424 StackFrameIterator it(isolate); 425 if (it.done()) { 426 PrintF("unknown location (no JavaScript frames present)"); 427 return; 428 } 429 StackFrame* raw_frame = it.frame(); 430 if (raw_frame->is_internal()) { 431 Code* apply_builtin = 432 isolate->builtins()->builtin(Builtins::kFunctionPrototypeApply); 433 if (raw_frame->unchecked_code() == apply_builtin) { 434 PrintF("apply from "); 435 it.Advance(); 436 raw_frame = it.frame(); 437 } 438 } 439 JavaScriptFrame::PrintTop(isolate, stdout, false, true); 440 } 441 442 443 // Base class for element handler implementations. Contains the 444 // the common logic for objects with different ElementsKinds. 445 // Subclasses must specialize method for which the element 446 // implementation differs from the base class implementation. 447 // 448 // This class is intended to be used in the following way: 449 // 450 // class SomeElementsAccessor : 451 // public ElementsAccessorBase<SomeElementsAccessor, 452 // BackingStoreClass> { 453 // ... 454 // } 455 // 456 // This is an example of the Curiously Recurring Template Pattern (see 457 // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use 458 // CRTP to guarantee aggressive compile time optimizations (i.e. inlining and 459 // specialization of SomeElementsAccessor methods). 460 template <typename ElementsAccessorSubclass, 461 typename ElementsTraitsParam> 462 class ElementsAccessorBase : public ElementsAccessor { 463 public: 464 explicit ElementsAccessorBase(const char* name) 465 : ElementsAccessor(name) { } 466 467 typedef ElementsTraitsParam ElementsTraits; 468 typedef typename ElementsTraitsParam::BackingStore BackingStore; 469 470 static ElementsKind kind() { return ElementsTraits::Kind; } 471 472 static void ValidateContents(Handle<JSObject> holder, int length) { 473 } 474 475 static void ValidateImpl(Handle<JSObject> holder) { 476 Handle<FixedArrayBase> fixed_array_base(holder->elements()); 477 if (!fixed_array_base->IsHeapObject()) return; 478 // Arrays that have been shifted in place can't be verified. 479 if (fixed_array_base->IsFiller()) return; 480 int length = 0; 481 if (holder->IsJSArray()) { 482 Object* length_obj = Handle<JSArray>::cast(holder)->length(); 483 if (length_obj->IsSmi()) { 484 length = Smi::cast(length_obj)->value(); 485 } 486 } else { 487 length = fixed_array_base->length(); 488 } 489 ElementsAccessorSubclass::ValidateContents(holder, length); 490 } 491 492 void Validate(Handle<JSObject> holder) final { 493 DisallowHeapAllocation no_gc; 494 ElementsAccessorSubclass::ValidateImpl(holder); 495 } 496 497 bool IsPacked(Handle<JSObject> holder, Handle<FixedArrayBase> backing_store, 498 uint32_t start, uint32_t end) final { 499 return ElementsAccessorSubclass::IsPackedImpl(holder, backing_store, start, 500 end); 501 } 502 503 static bool IsPackedImpl(Handle<JSObject> holder, 504 Handle<FixedArrayBase> backing_store, uint32_t start, 505 uint32_t end) { 506 if (IsFastPackedElementsKind(kind())) return true; 507 for (uint32_t i = start; i < end; i++) { 508 if (!ElementsAccessorSubclass::HasElementImpl(holder, i, backing_store, 509 ALL_PROPERTIES)) { 510 return false; 511 } 512 } 513 return true; 514 } 515 516 static void TryTransitionResultArrayToPacked(Handle<JSArray> array) { 517 if (!IsHoleyElementsKind(kind())) return; 518 int length = Smi::cast(array->length())->value(); 519 Handle<FixedArrayBase> backing_store(array->elements()); 520 if (!ElementsAccessorSubclass::IsPackedImpl(array, backing_store, 0, 521 length)) { 522 return; 523 } 524 ElementsKind packed_kind = GetPackedElementsKind(kind()); 525 Handle<Map> new_map = 526 JSObject::GetElementsTransitionMap(array, packed_kind); 527 JSObject::MigrateToMap(array, new_map); 528 if (FLAG_trace_elements_transitions) { 529 JSObject::PrintElementsTransition(stdout, array, kind(), backing_store, 530 packed_kind, backing_store); 531 } 532 } 533 534 bool HasElement(Handle<JSObject> holder, uint32_t index, 535 Handle<FixedArrayBase> backing_store, 536 PropertyFilter filter) final { 537 return ElementsAccessorSubclass::HasElementImpl(holder, index, 538 backing_store, filter); 539 } 540 541 static bool HasElementImpl(Handle<JSObject> holder, uint32_t index, 542 Handle<FixedArrayBase> backing_store, 543 PropertyFilter filter) { 544 return ElementsAccessorSubclass::GetEntryForIndexImpl( 545 *holder, *backing_store, index, filter) != kMaxUInt32; 546 } 547 548 Handle<Object> Get(Handle<FixedArrayBase> backing_store, 549 uint32_t entry) final { 550 return ElementsAccessorSubclass::GetImpl(backing_store, entry); 551 } 552 553 static Handle<Object> GetImpl(Handle<FixedArrayBase> backing_store, 554 uint32_t entry) { 555 uint32_t index = GetIndexForEntryImpl(*backing_store, entry); 556 return BackingStore::get(Handle<BackingStore>::cast(backing_store), index); 557 } 558 559 void Set(FixedArrayBase* backing_store, uint32_t entry, Object* value) final { 560 ElementsAccessorSubclass::SetImpl(backing_store, entry, value); 561 } 562 563 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 564 Object* value) { 565 UNREACHABLE(); 566 } 567 568 569 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 570 Object* value, WriteBarrierMode mode) { 571 UNREACHABLE(); 572 } 573 574 void Reconfigure(Handle<JSObject> object, Handle<FixedArrayBase> store, 575 uint32_t entry, Handle<Object> value, 576 PropertyAttributes attributes) final { 577 ElementsAccessorSubclass::ReconfigureImpl(object, store, entry, value, 578 attributes); 579 } 580 581 static void ReconfigureImpl(Handle<JSObject> object, 582 Handle<FixedArrayBase> store, uint32_t entry, 583 Handle<Object> value, 584 PropertyAttributes attributes) { 585 UNREACHABLE(); 586 } 587 588 void Add(Handle<JSObject> object, uint32_t index, Handle<Object> value, 589 PropertyAttributes attributes, uint32_t new_capacity) final { 590 ElementsAccessorSubclass::AddImpl(object, index, value, attributes, 591 new_capacity); 592 } 593 594 static void AddImpl(Handle<JSObject> object, uint32_t index, 595 Handle<Object> value, PropertyAttributes attributes, 596 uint32_t new_capacity) { 597 UNREACHABLE(); 598 } 599 600 uint32_t Push(Handle<JSArray> receiver, Handle<FixedArrayBase> backing_store, 601 Arguments* args, uint32_t push_size) final { 602 return ElementsAccessorSubclass::PushImpl(receiver, backing_store, args, 603 push_size); 604 } 605 606 static uint32_t PushImpl(Handle<JSArray> receiver, 607 Handle<FixedArrayBase> elms_obj, Arguments* args, 608 uint32_t push_sized) { 609 UNREACHABLE(); 610 return 0; 611 } 612 613 uint32_t Unshift(Handle<JSArray> receiver, 614 Handle<FixedArrayBase> backing_store, Arguments* args, 615 uint32_t unshift_size) final { 616 return ElementsAccessorSubclass::UnshiftImpl(receiver, backing_store, args, 617 unshift_size); 618 } 619 620 static uint32_t UnshiftImpl(Handle<JSArray> receiver, 621 Handle<FixedArrayBase> elms_obj, Arguments* args, 622 uint32_t unshift_size) { 623 UNREACHABLE(); 624 return 0; 625 } 626 627 Handle<JSArray> Slice(Handle<JSObject> receiver, 628 Handle<FixedArrayBase> backing_store, uint32_t start, 629 uint32_t end) final { 630 return ElementsAccessorSubclass::SliceImpl(receiver, backing_store, start, 631 end); 632 } 633 634 static Handle<JSArray> SliceImpl(Handle<JSObject> receiver, 635 Handle<FixedArrayBase> backing_store, 636 uint32_t start, uint32_t end) { 637 UNREACHABLE(); 638 return Handle<JSArray>(); 639 } 640 641 Handle<JSArray> Splice(Handle<JSArray> receiver, 642 Handle<FixedArrayBase> backing_store, uint32_t start, 643 uint32_t delete_count, Arguments* args, 644 uint32_t add_count) final { 645 return ElementsAccessorSubclass::SpliceImpl(receiver, backing_store, start, 646 delete_count, args, add_count); 647 } 648 649 static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver, 650 Handle<FixedArrayBase> backing_store, 651 uint32_t start, uint32_t delete_count, 652 Arguments* args, uint32_t add_count) { 653 UNREACHABLE(); 654 return Handle<JSArray>(); 655 } 656 657 Handle<Object> Pop(Handle<JSArray> receiver, 658 Handle<FixedArrayBase> backing_store) final { 659 return ElementsAccessorSubclass::PopImpl(receiver, backing_store); 660 } 661 662 static Handle<Object> PopImpl(Handle<JSArray> receiver, 663 Handle<FixedArrayBase> backing_store) { 664 UNREACHABLE(); 665 return Handle<Object>(); 666 } 667 668 Handle<Object> Shift(Handle<JSArray> receiver, 669 Handle<FixedArrayBase> backing_store) final { 670 return ElementsAccessorSubclass::ShiftImpl(receiver, backing_store); 671 } 672 673 static Handle<Object> ShiftImpl(Handle<JSArray> receiver, 674 Handle<FixedArrayBase> backing_store) { 675 UNREACHABLE(); 676 return Handle<Object>(); 677 } 678 679 void SetLength(Handle<JSArray> array, uint32_t length) final { 680 ElementsAccessorSubclass::SetLengthImpl(array->GetIsolate(), array, length, 681 handle(array->elements())); 682 } 683 684 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array, 685 uint32_t length, 686 Handle<FixedArrayBase> backing_store) { 687 DCHECK(!array->SetLengthWouldNormalize(length)); 688 DCHECK(IsFastElementsKind(array->GetElementsKind())); 689 uint32_t old_length = 0; 690 CHECK(array->length()->ToArrayIndex(&old_length)); 691 692 if (old_length < length) { 693 ElementsKind kind = array->GetElementsKind(); 694 if (!IsFastHoleyElementsKind(kind)) { 695 kind = GetHoleyElementsKind(kind); 696 JSObject::TransitionElementsKind(array, kind); 697 } 698 } 699 700 // Check whether the backing store should be shrunk. 701 uint32_t capacity = backing_store->length(); 702 old_length = Min(old_length, capacity); 703 if (length == 0) { 704 array->initialize_elements(); 705 } else if (length <= capacity) { 706 if (array->HasFastSmiOrObjectElements()) { 707 backing_store = JSObject::EnsureWritableFastElements(array); 708 } 709 if (2 * length <= capacity) { 710 // If more than half the elements won't be used, trim the array. 711 isolate->heap()->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>( 712 *backing_store, capacity - length); 713 } else { 714 // Otherwise, fill the unused tail with holes. 715 for (uint32_t i = length; i < old_length; i++) { 716 BackingStore::cast(*backing_store)->set_the_hole(i); 717 } 718 } 719 } else { 720 // Check whether the backing store should be expanded. 721 capacity = Max(length, JSObject::NewElementsCapacity(capacity)); 722 ElementsAccessorSubclass::GrowCapacityAndConvertImpl(array, capacity); 723 } 724 725 array->set_length(Smi::FromInt(length)); 726 JSObject::ValidateElements(array); 727 } 728 729 static Handle<FixedArrayBase> ConvertElementsWithCapacity( 730 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 731 ElementsKind from_kind, uint32_t capacity) { 732 return ConvertElementsWithCapacity( 733 object, old_elements, from_kind, capacity, 0, 0, 734 ElementsAccessor::kCopyToEndAndInitializeToHole); 735 } 736 737 static Handle<FixedArrayBase> ConvertElementsWithCapacity( 738 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 739 ElementsKind from_kind, uint32_t capacity, int copy_size) { 740 return ConvertElementsWithCapacity(object, old_elements, from_kind, 741 capacity, 0, 0, copy_size); 742 } 743 744 static Handle<FixedArrayBase> ConvertElementsWithCapacity( 745 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 746 ElementsKind from_kind, uint32_t capacity, uint32_t src_index, 747 uint32_t dst_index, int copy_size) { 748 Isolate* isolate = object->GetIsolate(); 749 Handle<FixedArrayBase> new_elements; 750 if (IsFastDoubleElementsKind(kind())) { 751 new_elements = isolate->factory()->NewFixedDoubleArray(capacity); 752 } else { 753 new_elements = isolate->factory()->NewUninitializedFixedArray(capacity); 754 } 755 756 int packed_size = kPackedSizeNotKnown; 757 if (IsFastPackedElementsKind(from_kind) && object->IsJSArray()) { 758 packed_size = Smi::cast(JSArray::cast(*object)->length())->value(); 759 } 760 761 ElementsAccessorSubclass::CopyElementsImpl( 762 *old_elements, src_index, *new_elements, from_kind, dst_index, 763 packed_size, copy_size); 764 765 return new_elements; 766 } 767 768 static void GrowCapacityAndConvertImpl(Handle<JSObject> object, 769 uint32_t capacity) { 770 ElementsKind from_kind = object->GetElementsKind(); 771 if (IsFastSmiOrObjectElementsKind(from_kind)) { 772 // Array optimizations rely on the prototype lookups of Array objects 773 // always returning undefined. If there is a store to the initial 774 // prototype object, make sure all of these optimizations are invalidated. 775 object->GetIsolate()->UpdateArrayProtectorOnSetLength(object); 776 } 777 Handle<FixedArrayBase> old_elements(object->elements()); 778 // This method should only be called if there's a reason to update the 779 // elements. 780 DCHECK(IsFastDoubleElementsKind(from_kind) != 781 IsFastDoubleElementsKind(kind()) || 782 IsDictionaryElementsKind(from_kind) || 783 static_cast<uint32_t>(old_elements->length()) < capacity); 784 Handle<FixedArrayBase> elements = 785 ConvertElementsWithCapacity(object, old_elements, from_kind, capacity); 786 787 ElementsKind to_kind = kind(); 788 if (IsHoleyElementsKind(from_kind)) to_kind = GetHoleyElementsKind(to_kind); 789 Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, to_kind); 790 JSObject::SetMapAndElements(object, new_map, elements); 791 792 // Transition through the allocation site as well if present. 793 JSObject::UpdateAllocationSite(object, to_kind); 794 795 if (FLAG_trace_elements_transitions) { 796 JSObject::PrintElementsTransition(stdout, object, from_kind, old_elements, 797 to_kind, elements); 798 } 799 } 800 801 void GrowCapacityAndConvert(Handle<JSObject> object, 802 uint32_t capacity) final { 803 ElementsAccessorSubclass::GrowCapacityAndConvertImpl(object, capacity); 804 } 805 806 void Delete(Handle<JSObject> obj, uint32_t entry) final { 807 ElementsAccessorSubclass::DeleteImpl(obj, entry); 808 } 809 810 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 811 FixedArrayBase* to, ElementsKind from_kind, 812 uint32_t to_start, int packed_size, 813 int copy_size) { 814 UNREACHABLE(); 815 } 816 817 void CopyElements(Handle<FixedArrayBase> from, uint32_t from_start, 818 ElementsKind from_kind, Handle<FixedArrayBase> to, 819 uint32_t to_start, int copy_size) final { 820 DCHECK(!from.is_null()); 821 // NOTE: the ElementsAccessorSubclass::CopyElementsImpl() methods 822 // violate the handlified function signature convention: 823 // raw pointer parameters in the function that allocates. This is done 824 // intentionally to avoid ArrayConcat() builtin performance degradation. 825 // See the comment in another ElementsAccessorBase::CopyElements() for 826 // details. 827 ElementsAccessorSubclass::CopyElementsImpl(*from, from_start, *to, 828 from_kind, to_start, 829 kPackedSizeNotKnown, copy_size); 830 } 831 832 void CopyElements(JSObject* from_holder, uint32_t from_start, 833 ElementsKind from_kind, Handle<FixedArrayBase> to, 834 uint32_t to_start, int copy_size) final { 835 int packed_size = kPackedSizeNotKnown; 836 bool is_packed = IsFastPackedElementsKind(from_kind) && 837 from_holder->IsJSArray(); 838 if (is_packed) { 839 packed_size = 840 Smi::cast(JSArray::cast(from_holder)->length())->value(); 841 if (copy_size >= 0 && packed_size > copy_size) { 842 packed_size = copy_size; 843 } 844 } 845 FixedArrayBase* from = from_holder->elements(); 846 // NOTE: the ElementsAccessorSubclass::CopyElementsImpl() methods 847 // violate the handlified function signature convention: 848 // raw pointer parameters in the function that allocates. This is done 849 // intentionally to avoid ArrayConcat() builtin performance degradation. 850 // 851 // Details: The idea is that allocations actually happen only in case of 852 // copying from object with fast double elements to object with object 853 // elements. In all the other cases there are no allocations performed and 854 // handle creation causes noticeable performance degradation of the builtin. 855 ElementsAccessorSubclass::CopyElementsImpl( 856 from, from_start, *to, from_kind, to_start, packed_size, copy_size); 857 } 858 859 static void CollectElementIndicesImpl(Handle<JSObject> object, 860 Handle<FixedArrayBase> backing_store, 861 KeyAccumulator* keys, uint32_t range, 862 PropertyFilter filter, 863 uint32_t offset) { 864 if (filter & ONLY_ALL_CAN_READ) { 865 // Non-dictionary elements can't have all-can-read accessors. 866 return; 867 } 868 uint32_t length = 0; 869 if (object->IsJSArray()) { 870 length = Smi::cast(JSArray::cast(*object)->length())->value(); 871 } else { 872 length = 873 ElementsAccessorSubclass::GetCapacityImpl(*object, *backing_store); 874 } 875 if (range < length) length = range; 876 for (uint32_t i = offset; i < length; i++) { 877 if (!ElementsAccessorSubclass::HasElementImpl(object, i, backing_store, 878 filter)) 879 continue; 880 keys->AddKey(i); 881 } 882 } 883 884 void CollectElementIndices(Handle<JSObject> object, 885 Handle<FixedArrayBase> backing_store, 886 KeyAccumulator* keys, uint32_t range, 887 PropertyFilter filter, uint32_t offset) final { 888 ElementsAccessorSubclass::CollectElementIndicesImpl( 889 object, backing_store, keys, range, filter, offset); 890 }; 891 892 void AddElementsToKeyAccumulator(Handle<JSObject> receiver, 893 KeyAccumulator* accumulator, 894 AddKeyConversion convert) final { 895 Handle<FixedArrayBase> from(receiver->elements()); 896 uint32_t add_length = 897 ElementsAccessorSubclass::GetCapacityImpl(*receiver, *from); 898 if (add_length == 0) return; 899 900 for (uint32_t i = 0; i < add_length; i++) { 901 if (!ElementsAccessorSubclass::HasEntryImpl(*from, i)) continue; 902 Handle<Object> value = ElementsAccessorSubclass::GetImpl(from, i); 903 DCHECK(!value->IsTheHole()); 904 DCHECK(!value->IsAccessorPair()); 905 DCHECK(!value->IsExecutableAccessorInfo()); 906 accumulator->AddKey(value, convert); 907 } 908 } 909 910 static uint32_t GetCapacityImpl(JSObject* holder, 911 FixedArrayBase* backing_store) { 912 return backing_store->length(); 913 } 914 915 uint32_t GetCapacity(JSObject* holder, FixedArrayBase* backing_store) final { 916 return ElementsAccessorSubclass::GetCapacityImpl(holder, backing_store); 917 } 918 919 static bool HasEntryImpl(FixedArrayBase* backing_store, uint32_t entry) { 920 return true; 921 } 922 923 static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store, 924 uint32_t entry) { 925 return entry; 926 } 927 928 static uint32_t GetEntryForIndexImpl(JSObject* holder, 929 FixedArrayBase* backing_store, 930 uint32_t index, PropertyFilter filter) { 931 if (IsHoleyElementsKind(kind())) { 932 return index < ElementsAccessorSubclass::GetCapacityImpl(holder, 933 backing_store) && 934 !BackingStore::cast(backing_store)->is_the_hole(index) 935 ? index 936 : kMaxUInt32; 937 } else { 938 uint32_t length = 939 holder->IsJSArray() 940 ? static_cast<uint32_t>( 941 Smi::cast(JSArray::cast(holder)->length())->value()) 942 : ElementsAccessorSubclass::GetCapacityImpl(holder, 943 backing_store); 944 return index < length ? index : kMaxUInt32; 945 } 946 } 947 948 uint32_t GetEntryForIndex(JSObject* holder, FixedArrayBase* backing_store, 949 uint32_t index) final { 950 return ElementsAccessorSubclass::GetEntryForIndexImpl( 951 holder, backing_store, index, ALL_PROPERTIES); 952 } 953 954 static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, 955 uint32_t entry) { 956 return PropertyDetails(NONE, DATA, 0, PropertyCellType::kNoCell); 957 } 958 959 PropertyDetails GetDetails(FixedArrayBase* backing_store, 960 uint32_t entry) final { 961 return ElementsAccessorSubclass::GetDetailsImpl(backing_store, entry); 962 } 963 964 private: 965 DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase); 966 }; 967 968 969 class DictionaryElementsAccessor 970 : public ElementsAccessorBase<DictionaryElementsAccessor, 971 ElementsKindTraits<DICTIONARY_ELEMENTS> > { 972 public: 973 explicit DictionaryElementsAccessor(const char* name) 974 : ElementsAccessorBase<DictionaryElementsAccessor, 975 ElementsKindTraits<DICTIONARY_ELEMENTS> >(name) {} 976 977 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array, 978 uint32_t length, 979 Handle<FixedArrayBase> backing_store) { 980 Handle<SeededNumberDictionary> dict = 981 Handle<SeededNumberDictionary>::cast(backing_store); 982 int capacity = dict->Capacity(); 983 uint32_t old_length = 0; 984 CHECK(array->length()->ToArrayLength(&old_length)); 985 if (length < old_length) { 986 if (dict->requires_slow_elements()) { 987 // Find last non-deletable element in range of elements to be 988 // deleted and adjust range accordingly. 989 for (int entry = 0; entry < capacity; entry++) { 990 DisallowHeapAllocation no_gc; 991 Object* index = dict->KeyAt(entry); 992 if (index->IsNumber()) { 993 uint32_t number = static_cast<uint32_t>(index->Number()); 994 if (length <= number && number < old_length) { 995 PropertyDetails details = dict->DetailsAt(entry); 996 if (!details.IsConfigurable()) length = number + 1; 997 } 998 } 999 } 1000 } 1001 1002 if (length == 0) { 1003 // Flush the backing store. 1004 JSObject::ResetElements(array); 1005 } else { 1006 DisallowHeapAllocation no_gc; 1007 // Remove elements that should be deleted. 1008 int removed_entries = 0; 1009 Handle<Object> the_hole_value = isolate->factory()->the_hole_value(); 1010 for (int entry = 0; entry < capacity; entry++) { 1011 Object* index = dict->KeyAt(entry); 1012 if (index->IsNumber()) { 1013 uint32_t number = static_cast<uint32_t>(index->Number()); 1014 if (length <= number && number < old_length) { 1015 dict->SetEntry(entry, the_hole_value, the_hole_value); 1016 removed_entries++; 1017 } 1018 } 1019 } 1020 1021 // Update the number of elements. 1022 dict->ElementsRemoved(removed_entries); 1023 } 1024 } 1025 1026 Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length); 1027 array->set_length(*length_obj); 1028 } 1029 1030 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 1031 FixedArrayBase* to, ElementsKind from_kind, 1032 uint32_t to_start, int packed_size, 1033 int copy_size) { 1034 UNREACHABLE(); 1035 } 1036 1037 1038 static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) { 1039 // TODO(verwaest): Remove reliance on index in Shrink. 1040 Handle<SeededNumberDictionary> dict( 1041 SeededNumberDictionary::cast(obj->elements())); 1042 uint32_t index = GetIndexForEntryImpl(*dict, entry); 1043 Handle<Object> result = SeededNumberDictionary::DeleteProperty(dict, entry); 1044 USE(result); 1045 DCHECK(result->IsTrue()); 1046 Handle<FixedArray> new_elements = 1047 SeededNumberDictionary::Shrink(dict, index); 1048 obj->set_elements(*new_elements); 1049 } 1050 1051 static Object* GetRaw(FixedArrayBase* store, uint32_t entry) { 1052 SeededNumberDictionary* backing_store = SeededNumberDictionary::cast(store); 1053 return backing_store->ValueAt(entry); 1054 } 1055 1056 static Handle<Object> GetImpl(Handle<FixedArrayBase> store, uint32_t entry) { 1057 Isolate* isolate = store->GetIsolate(); 1058 return handle(GetRaw(*store, entry), isolate); 1059 } 1060 1061 static inline void SetImpl(FixedArrayBase* store, uint32_t entry, 1062 Object* value) { 1063 SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(store); 1064 dictionary->ValueAtPut(entry, value); 1065 } 1066 1067 static void ReconfigureImpl(Handle<JSObject> object, 1068 Handle<FixedArrayBase> store, uint32_t entry, 1069 Handle<Object> value, 1070 PropertyAttributes attributes) { 1071 SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(*store); 1072 if (attributes != NONE) object->RequireSlowElements(dictionary); 1073 dictionary->ValueAtPut(entry, *value); 1074 PropertyDetails details = dictionary->DetailsAt(entry); 1075 details = PropertyDetails(attributes, DATA, details.dictionary_index(), 1076 PropertyCellType::kNoCell); 1077 dictionary->DetailsAtPut(entry, details); 1078 } 1079 1080 static void AddImpl(Handle<JSObject> object, uint32_t index, 1081 Handle<Object> value, PropertyAttributes attributes, 1082 uint32_t new_capacity) { 1083 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); 1084 Handle<SeededNumberDictionary> dictionary = 1085 object->HasFastElements() 1086 ? JSObject::NormalizeElements(object) 1087 : handle(SeededNumberDictionary::cast(object->elements())); 1088 Handle<SeededNumberDictionary> new_dictionary = 1089 SeededNumberDictionary::AddNumberEntry( 1090 dictionary, index, value, details, 1091 object->map()->is_prototype_map()); 1092 if (attributes != NONE) object->RequireSlowElements(*new_dictionary); 1093 if (dictionary.is_identical_to(new_dictionary)) return; 1094 object->set_elements(*new_dictionary); 1095 } 1096 1097 static bool HasEntryImpl(FixedArrayBase* store, uint32_t entry) { 1098 DisallowHeapAllocation no_gc; 1099 SeededNumberDictionary* dict = SeededNumberDictionary::cast(store); 1100 Object* index = dict->KeyAt(entry); 1101 return !index->IsTheHole(); 1102 } 1103 1104 static uint32_t GetIndexForEntryImpl(FixedArrayBase* store, uint32_t entry) { 1105 DisallowHeapAllocation no_gc; 1106 SeededNumberDictionary* dict = SeededNumberDictionary::cast(store); 1107 uint32_t result = 0; 1108 CHECK(dict->KeyAt(entry)->ToArrayIndex(&result)); 1109 return result; 1110 } 1111 1112 static uint32_t GetEntryForIndexImpl(JSObject* holder, FixedArrayBase* store, 1113 uint32_t index, PropertyFilter filter) { 1114 DisallowHeapAllocation no_gc; 1115 SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(store); 1116 int entry = dictionary->FindEntry(index); 1117 if (entry == SeededNumberDictionary::kNotFound) return kMaxUInt32; 1118 if (filter != ALL_PROPERTIES) { 1119 PropertyDetails details = dictionary->DetailsAt(entry); 1120 PropertyAttributes attr = details.attributes(); 1121 if ((attr & filter) != 0) return kMaxUInt32; 1122 } 1123 return static_cast<uint32_t>(entry); 1124 } 1125 1126 static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, 1127 uint32_t entry) { 1128 return SeededNumberDictionary::cast(backing_store)->DetailsAt(entry); 1129 } 1130 1131 static void CollectElementIndicesImpl(Handle<JSObject> object, 1132 Handle<FixedArrayBase> backing_store, 1133 KeyAccumulator* keys, uint32_t range, 1134 PropertyFilter filter, 1135 uint32_t offset) { 1136 Handle<SeededNumberDictionary> dictionary = 1137 Handle<SeededNumberDictionary>::cast(backing_store); 1138 int capacity = dictionary->Capacity(); 1139 for (int i = 0; i < capacity; i++) { 1140 Object* k = dictionary->KeyAt(i); 1141 if (!dictionary->IsKey(k)) continue; 1142 if (k->FilterKey(filter)) continue; 1143 if (dictionary->IsDeleted(i)) continue; 1144 DCHECK(k->IsNumber()); 1145 DCHECK_LE(k->Number(), kMaxUInt32); 1146 uint32_t index = static_cast<uint32_t>(k->Number()); 1147 if (index < offset) continue; 1148 PropertyDetails details = dictionary->DetailsAt(i); 1149 if (filter & ONLY_ALL_CAN_READ) { 1150 if (details.kind() != kAccessor) continue; 1151 Object* accessors = dictionary->ValueAt(i); 1152 if (!accessors->IsAccessorInfo()) continue; 1153 if (!AccessorInfo::cast(accessors)->all_can_read()) continue; 1154 } 1155 PropertyAttributes attr = details.attributes(); 1156 if ((attr & filter) != 0) continue; 1157 keys->AddKey(index); 1158 } 1159 1160 keys->SortCurrentElementsList(); 1161 } 1162 }; 1163 1164 1165 // Super class for all fast element arrays. 1166 template<typename FastElementsAccessorSubclass, 1167 typename KindTraits> 1168 class FastElementsAccessor 1169 : public ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits> { 1170 public: 1171 explicit FastElementsAccessor(const char* name) 1172 : ElementsAccessorBase<FastElementsAccessorSubclass, 1173 KindTraits>(name) {} 1174 1175 typedef typename KindTraits::BackingStore BackingStore; 1176 1177 static void DeleteAtEnd(Handle<JSObject> obj, 1178 Handle<BackingStore> backing_store, uint32_t entry) { 1179 uint32_t length = static_cast<uint32_t>(backing_store->length()); 1180 Heap* heap = obj->GetHeap(); 1181 for (; entry > 0; entry--) { 1182 if (!backing_store->is_the_hole(entry - 1)) break; 1183 } 1184 if (entry == 0) { 1185 FixedArray* empty = heap->empty_fixed_array(); 1186 if (obj->HasFastArgumentsElements()) { 1187 FixedArray::cast(obj->elements())->set(1, empty); 1188 } else { 1189 obj->set_elements(empty); 1190 } 1191 return; 1192 } 1193 1194 heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(*backing_store, 1195 length - entry); 1196 } 1197 1198 static void DeleteCommon(Handle<JSObject> obj, uint32_t entry, 1199 Handle<FixedArrayBase> store) { 1200 DCHECK(obj->HasFastSmiOrObjectElements() || 1201 obj->HasFastDoubleElements() || 1202 obj->HasFastArgumentsElements()); 1203 Handle<BackingStore> backing_store = Handle<BackingStore>::cast(store); 1204 if (!obj->IsJSArray() && 1205 entry == static_cast<uint32_t>(store->length()) - 1) { 1206 DeleteAtEnd(obj, backing_store, entry); 1207 return; 1208 } 1209 1210 backing_store->set_the_hole(entry); 1211 1212 // TODO(verwaest): Move this out of elements.cc. 1213 // If an old space backing store is larger than a certain size and 1214 // has too few used values, normalize it. 1215 // To avoid doing the check on every delete we require at least 1216 // one adjacent hole to the value being deleted. 1217 const int kMinLengthForSparsenessCheck = 64; 1218 if (backing_store->length() < kMinLengthForSparsenessCheck) return; 1219 if (backing_store->GetHeap()->InNewSpace(*backing_store)) return; 1220 uint32_t length = 0; 1221 if (obj->IsJSArray()) { 1222 JSArray::cast(*obj)->length()->ToArrayLength(&length); 1223 } else { 1224 length = static_cast<uint32_t>(store->length()); 1225 } 1226 if ((entry > 0 && backing_store->is_the_hole(entry - 1)) || 1227 (entry + 1 < length && backing_store->is_the_hole(entry + 1))) { 1228 if (!obj->IsJSArray()) { 1229 uint32_t i; 1230 for (i = entry + 1; i < length; i++) { 1231 if (!backing_store->is_the_hole(i)) break; 1232 } 1233 if (i == length) { 1234 DeleteAtEnd(obj, backing_store, entry); 1235 return; 1236 } 1237 } 1238 int num_used = 0; 1239 for (int i = 0; i < backing_store->length(); ++i) { 1240 if (!backing_store->is_the_hole(i)) { 1241 ++num_used; 1242 // Bail out if a number dictionary wouldn't be able to save at least 1243 // 75% space. 1244 if (4 * SeededNumberDictionary::ComputeCapacity(num_used) * 1245 SeededNumberDictionary::kEntrySize > 1246 backing_store->length()) { 1247 return; 1248 } 1249 } 1250 } 1251 JSObject::NormalizeElements(obj); 1252 } 1253 } 1254 1255 static void ReconfigureImpl(Handle<JSObject> object, 1256 Handle<FixedArrayBase> store, uint32_t entry, 1257 Handle<Object> value, 1258 PropertyAttributes attributes) { 1259 Handle<SeededNumberDictionary> dictionary = 1260 JSObject::NormalizeElements(object); 1261 entry = dictionary->FindEntry(entry); 1262 DictionaryElementsAccessor::ReconfigureImpl(object, dictionary, entry, 1263 value, attributes); 1264 } 1265 1266 static void AddImpl(Handle<JSObject> object, uint32_t index, 1267 Handle<Object> value, PropertyAttributes attributes, 1268 uint32_t new_capacity) { 1269 DCHECK_EQ(NONE, attributes); 1270 ElementsKind from_kind = object->GetElementsKind(); 1271 ElementsKind to_kind = FastElementsAccessorSubclass::kind(); 1272 if (IsDictionaryElementsKind(from_kind) || 1273 IsFastDoubleElementsKind(from_kind) != 1274 IsFastDoubleElementsKind(to_kind) || 1275 FastElementsAccessorSubclass::GetCapacityImpl( 1276 *object, object->elements()) != new_capacity) { 1277 FastElementsAccessorSubclass::GrowCapacityAndConvertImpl(object, 1278 new_capacity); 1279 } else { 1280 if (from_kind != to_kind) { 1281 JSObject::TransitionElementsKind(object, to_kind); 1282 } 1283 if (IsFastSmiOrObjectElementsKind(from_kind)) { 1284 DCHECK(IsFastSmiOrObjectElementsKind(to_kind)); 1285 JSObject::EnsureWritableFastElements(object); 1286 } 1287 } 1288 FastElementsAccessorSubclass::SetImpl(object->elements(), index, *value); 1289 } 1290 1291 static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) { 1292 ElementsKind kind = KindTraits::Kind; 1293 if (IsFastPackedElementsKind(kind)) { 1294 JSObject::TransitionElementsKind(obj, GetHoleyElementsKind(kind)); 1295 } 1296 if (IsFastSmiOrObjectElementsKind(KindTraits::Kind)) { 1297 JSObject::EnsureWritableFastElements(obj); 1298 } 1299 DeleteCommon(obj, entry, handle(obj->elements())); 1300 } 1301 1302 static bool HasEntryImpl(FixedArrayBase* backing_store, uint32_t entry) { 1303 return !BackingStore::cast(backing_store)->is_the_hole(entry); 1304 } 1305 1306 static void ValidateContents(Handle<JSObject> holder, int length) { 1307 #if DEBUG 1308 Isolate* isolate = holder->GetIsolate(); 1309 HandleScope scope(isolate); 1310 Handle<FixedArrayBase> elements(holder->elements(), isolate); 1311 Map* map = elements->map(); 1312 DCHECK((IsFastSmiOrObjectElementsKind(KindTraits::Kind) && 1313 (map == isolate->heap()->fixed_array_map() || 1314 map == isolate->heap()->fixed_cow_array_map())) || 1315 (IsFastDoubleElementsKind(KindTraits::Kind) == 1316 ((map == isolate->heap()->fixed_array_map() && length == 0) || 1317 map == isolate->heap()->fixed_double_array_map()))); 1318 if (length == 0) return; // nothing to do! 1319 DisallowHeapAllocation no_gc; 1320 Handle<BackingStore> backing_store = Handle<BackingStore>::cast(elements); 1321 if (IsFastSmiElementsKind(KindTraits::Kind)) { 1322 for (int i = 0; i < length; i++) { 1323 DCHECK(BackingStore::get(backing_store, i)->IsSmi() || 1324 (IsFastHoleyElementsKind(KindTraits::Kind) && 1325 backing_store->is_the_hole(i))); 1326 } 1327 } 1328 #endif 1329 } 1330 1331 static Handle<Object> PopImpl(Handle<JSArray> receiver, 1332 Handle<FixedArrayBase> backing_store) { 1333 return FastElementsAccessorSubclass::RemoveElement(receiver, backing_store, 1334 AT_END); 1335 } 1336 1337 static Handle<Object> ShiftImpl(Handle<JSArray> receiver, 1338 Handle<FixedArrayBase> backing_store) { 1339 return FastElementsAccessorSubclass::RemoveElement(receiver, backing_store, 1340 AT_START); 1341 } 1342 1343 static uint32_t PushImpl(Handle<JSArray> receiver, 1344 Handle<FixedArrayBase> backing_store, 1345 Arguments* args, uint32_t push_size) { 1346 return FastElementsAccessorSubclass::AddArguments(receiver, backing_store, 1347 args, push_size, AT_END); 1348 } 1349 1350 static uint32_t UnshiftImpl(Handle<JSArray> receiver, 1351 Handle<FixedArrayBase> backing_store, 1352 Arguments* args, uint32_t unshift_size) { 1353 return FastElementsAccessorSubclass::AddArguments( 1354 receiver, backing_store, args, unshift_size, AT_START); 1355 } 1356 1357 static void MoveElements(Isolate* isolate, Handle<JSArray> receiver, 1358 Handle<FixedArrayBase> backing_store, int dst_index, 1359 int src_index, int len, int hole_start, 1360 int hole_end) { 1361 UNREACHABLE(); 1362 } 1363 1364 static Handle<JSArray> SliceImpl(Handle<JSObject> receiver, 1365 Handle<FixedArrayBase> backing_store, 1366 uint32_t start, uint32_t end) { 1367 DCHECK(start < end); 1368 Isolate* isolate = receiver->GetIsolate(); 1369 int result_len = end - start; 1370 Handle<JSArray> result_array = isolate->factory()->NewJSArray( 1371 KindTraits::Kind, result_len, result_len); 1372 DisallowHeapAllocation no_gc; 1373 FastElementsAccessorSubclass::CopyElementsImpl( 1374 *backing_store, start, result_array->elements(), KindTraits::Kind, 0, 1375 kPackedSizeNotKnown, result_len); 1376 FastElementsAccessorSubclass::TryTransitionResultArrayToPacked( 1377 result_array); 1378 return result_array; 1379 } 1380 1381 static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver, 1382 Handle<FixedArrayBase> backing_store, 1383 uint32_t start, uint32_t delete_count, 1384 Arguments* args, uint32_t add_count) { 1385 Isolate* isolate = receiver->GetIsolate(); 1386 Heap* heap = isolate->heap(); 1387 uint32_t length = Smi::cast(receiver->length())->value(); 1388 uint32_t new_length = length - delete_count + add_count; 1389 1390 if (new_length == 0) { 1391 receiver->set_elements(heap->empty_fixed_array()); 1392 receiver->set_length(Smi::FromInt(0)); 1393 return isolate->factory()->NewJSArrayWithElements( 1394 backing_store, KindTraits::Kind, delete_count); 1395 } 1396 1397 // Construct the result array which holds the deleted elements. 1398 Handle<JSArray> deleted_elements = isolate->factory()->NewJSArray( 1399 KindTraits::Kind, delete_count, delete_count); 1400 if (delete_count > 0) { 1401 DisallowHeapAllocation no_gc; 1402 FastElementsAccessorSubclass::CopyElementsImpl( 1403 *backing_store, start, deleted_elements->elements(), KindTraits::Kind, 1404 0, kPackedSizeNotKnown, delete_count); 1405 } 1406 1407 // Delete and move elements to make space for add_count new elements. 1408 if (add_count < delete_count) { 1409 FastElementsAccessorSubclass::SpliceShrinkStep( 1410 isolate, receiver, backing_store, start, delete_count, add_count, 1411 length, new_length); 1412 } else if (add_count > delete_count) { 1413 backing_store = FastElementsAccessorSubclass::SpliceGrowStep( 1414 isolate, receiver, backing_store, start, delete_count, add_count, 1415 length, new_length); 1416 } 1417 1418 // Copy over the arguments. 1419 FastElementsAccessorSubclass::CopyArguments(args, backing_store, add_count, 1420 3, start); 1421 1422 receiver->set_length(Smi::FromInt(new_length)); 1423 FastElementsAccessorSubclass::TryTransitionResultArrayToPacked( 1424 deleted_elements); 1425 return deleted_elements; 1426 } 1427 1428 private: 1429 // SpliceShrinkStep might modify the backing_store. 1430 static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver, 1431 Handle<FixedArrayBase> backing_store, 1432 uint32_t start, uint32_t delete_count, 1433 uint32_t add_count, uint32_t len, 1434 uint32_t new_length) { 1435 const int move_left_count = len - delete_count - start; 1436 const int move_left_dst_index = start + add_count; 1437 FastElementsAccessorSubclass::MoveElements( 1438 isolate, receiver, backing_store, move_left_dst_index, 1439 start + delete_count, move_left_count, new_length, len); 1440 } 1441 1442 // SpliceGrowStep might modify the backing_store. 1443 static Handle<FixedArrayBase> SpliceGrowStep( 1444 Isolate* isolate, Handle<JSArray> receiver, 1445 Handle<FixedArrayBase> backing_store, uint32_t start, 1446 uint32_t delete_count, uint32_t add_count, uint32_t length, 1447 uint32_t new_length) { 1448 // Check we do not overflow the new_length. 1449 DCHECK((add_count - delete_count) <= (Smi::kMaxValue - length)); 1450 // Check if backing_store is big enough. 1451 if (new_length <= static_cast<uint32_t>(backing_store->length())) { 1452 FastElementsAccessorSubclass::MoveElements( 1453 isolate, receiver, backing_store, start + add_count, 1454 start + delete_count, (length - delete_count - start), 0, 0); 1455 // MoveElements updates the backing_store in-place. 1456 return backing_store; 1457 } 1458 // New backing storage is needed. 1459 int capacity = JSObject::NewElementsCapacity(new_length); 1460 // Partially copy all elements up to start. 1461 Handle<FixedArrayBase> new_elms = 1462 FastElementsAccessorSubclass::ConvertElementsWithCapacity( 1463 receiver, backing_store, KindTraits::Kind, capacity, start); 1464 // Copy the trailing elements after start + delete_count 1465 FastElementsAccessorSubclass::CopyElementsImpl( 1466 *backing_store, start + delete_count, *new_elms, KindTraits::Kind, 1467 start + add_count, kPackedSizeNotKnown, 1468 ElementsAccessor::kCopyToEndAndInitializeToHole); 1469 receiver->set_elements(*new_elms); 1470 return new_elms; 1471 } 1472 1473 static Handle<Object> RemoveElement(Handle<JSArray> receiver, 1474 Handle<FixedArrayBase> backing_store, 1475 Where remove_position) { 1476 Isolate* isolate = receiver->GetIsolate(); 1477 uint32_t length = 1478 static_cast<uint32_t>(Smi::cast(receiver->length())->value()); 1479 DCHECK(length > 0); 1480 int new_length = length - 1; 1481 int remove_index = remove_position == AT_START ? 0 : new_length; 1482 Handle<Object> result = 1483 FastElementsAccessorSubclass::GetImpl(backing_store, remove_index); 1484 if (remove_position == AT_START) { 1485 FastElementsAccessorSubclass::MoveElements( 1486 isolate, receiver, backing_store, 0, 1, new_length, 0, 0); 1487 } 1488 FastElementsAccessorSubclass::SetLengthImpl(isolate, receiver, new_length, 1489 backing_store); 1490 1491 if (IsHoleyElementsKind(KindTraits::Kind) && result->IsTheHole()) { 1492 return receiver->GetIsolate()->factory()->undefined_value(); 1493 } 1494 return result; 1495 } 1496 1497 static uint32_t AddArguments(Handle<JSArray> receiver, 1498 Handle<FixedArrayBase> backing_store, 1499 Arguments* args, uint32_t add_size, 1500 Where remove_position) { 1501 uint32_t length = Smi::cast(receiver->length())->value(); 1502 DCHECK(add_size > 0); 1503 uint32_t elms_len = backing_store->length(); 1504 // Check we do not overflow the new_length. 1505 DCHECK(add_size <= static_cast<uint32_t>(Smi::kMaxValue - length)); 1506 uint32_t new_length = length + add_size; 1507 1508 if (new_length > elms_len) { 1509 // New backing storage is needed. 1510 uint32_t capacity = JSObject::NewElementsCapacity(new_length); 1511 // If we add arguments to the start we have to shift the existing objects. 1512 int copy_dst_index = remove_position == AT_START ? add_size : 0; 1513 // Copy over all objects to a new backing_store. 1514 backing_store = FastElementsAccessorSubclass::ConvertElementsWithCapacity( 1515 receiver, backing_store, KindTraits::Kind, capacity, 0, 1516 copy_dst_index, ElementsAccessor::kCopyToEndAndInitializeToHole); 1517 receiver->set_elements(*backing_store); 1518 } else if (remove_position == AT_START) { 1519 // If the backing store has enough capacity and we add elements to the 1520 // start we have to shift the existing objects. 1521 Isolate* isolate = receiver->GetIsolate(); 1522 FastElementsAccessorSubclass::MoveElements( 1523 isolate, receiver, backing_store, add_size, 0, length, 0, 0); 1524 } 1525 1526 int insertion_index = remove_position == AT_START ? 0 : length; 1527 // Copy the arguments to the start. 1528 FastElementsAccessorSubclass::CopyArguments(args, backing_store, add_size, 1529 1, insertion_index); 1530 // Set the length. 1531 receiver->set_length(Smi::FromInt(new_length)); 1532 return new_length; 1533 } 1534 1535 static void CopyArguments(Arguments* args, Handle<FixedArrayBase> dst_store, 1536 uint32_t copy_size, uint32_t src_index, 1537 uint32_t dst_index) { 1538 // Add the provided values. 1539 DisallowHeapAllocation no_gc; 1540 FixedArrayBase* raw_backing_store = *dst_store; 1541 WriteBarrierMode mode = raw_backing_store->GetWriteBarrierMode(no_gc); 1542 for (uint32_t i = 0; i < copy_size; i++) { 1543 Object* argument = (*args)[i + src_index]; 1544 FastElementsAccessorSubclass::SetImpl(raw_backing_store, i + dst_index, 1545 argument, mode); 1546 } 1547 } 1548 }; 1549 1550 1551 template<typename FastElementsAccessorSubclass, 1552 typename KindTraits> 1553 class FastSmiOrObjectElementsAccessor 1554 : public FastElementsAccessor<FastElementsAccessorSubclass, KindTraits> { 1555 public: 1556 explicit FastSmiOrObjectElementsAccessor(const char* name) 1557 : FastElementsAccessor<FastElementsAccessorSubclass, 1558 KindTraits>(name) {} 1559 1560 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 1561 Object* value) { 1562 FixedArray::cast(backing_store)->set(entry, value); 1563 } 1564 1565 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 1566 Object* value, WriteBarrierMode mode) { 1567 FixedArray::cast(backing_store)->set(entry, value, mode); 1568 } 1569 1570 static Object* GetRaw(FixedArray* backing_store, uint32_t entry) { 1571 uint32_t index = FastElementsAccessorSubclass::GetIndexForEntryImpl( 1572 backing_store, entry); 1573 return backing_store->get(index); 1574 } 1575 1576 static void MoveElements(Isolate* isolate, Handle<JSArray> receiver, 1577 Handle<FixedArrayBase> backing_store, int dst_index, 1578 int src_index, int len, int hole_start, 1579 int hole_end) { 1580 Heap* heap = isolate->heap(); 1581 Handle<FixedArray> dst_elms = Handle<FixedArray>::cast(backing_store); 1582 if (heap->CanMoveObjectStart(*dst_elms) && dst_index == 0) { 1583 // Update all the copies of this backing_store handle. 1584 *dst_elms.location() = 1585 FixedArray::cast(heap->LeftTrimFixedArray(*dst_elms, src_index)); 1586 receiver->set_elements(*dst_elms); 1587 // Adjust the hole offset as the array has been shrunk. 1588 hole_end -= src_index; 1589 DCHECK_LE(hole_start, backing_store->length()); 1590 DCHECK_LE(hole_end, backing_store->length()); 1591 } else if (len != 0) { 1592 DisallowHeapAllocation no_gc; 1593 heap->MoveElements(*dst_elms, dst_index, src_index, len); 1594 } 1595 if (hole_start != hole_end) { 1596 dst_elms->FillWithHoles(hole_start, hole_end); 1597 } 1598 } 1599 1600 // NOTE: this method violates the handlified function signature convention: 1601 // raw pointer parameters in the function that allocates. 1602 // See ElementsAccessor::CopyElements() for details. 1603 // This method could actually allocate if copying from double elements to 1604 // object elements. 1605 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 1606 FixedArrayBase* to, ElementsKind from_kind, 1607 uint32_t to_start, int packed_size, 1608 int copy_size) { 1609 DisallowHeapAllocation no_gc; 1610 ElementsKind to_kind = KindTraits::Kind; 1611 switch (from_kind) { 1612 case FAST_SMI_ELEMENTS: 1613 case FAST_HOLEY_SMI_ELEMENTS: 1614 case FAST_ELEMENTS: 1615 case FAST_HOLEY_ELEMENTS: 1616 CopyObjectToObjectElements(from, from_kind, from_start, to, to_kind, 1617 to_start, copy_size); 1618 break; 1619 case FAST_DOUBLE_ELEMENTS: 1620 case FAST_HOLEY_DOUBLE_ELEMENTS: { 1621 AllowHeapAllocation allow_allocation; 1622 DCHECK(IsFastObjectElementsKind(to_kind)); 1623 CopyDoubleToObjectElements(from, from_start, to, to_start, copy_size); 1624 break; 1625 } 1626 case DICTIONARY_ELEMENTS: 1627 CopyDictionaryToObjectElements(from, from_start, to, to_kind, to_start, 1628 copy_size); 1629 break; 1630 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: 1631 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: 1632 UNREACHABLE(); 1633 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ 1634 case TYPE##_ELEMENTS: \ 1635 UNREACHABLE(); 1636 TYPED_ARRAYS(TYPED_ARRAY_CASE) 1637 #undef TYPED_ARRAY_CASE 1638 } 1639 } 1640 }; 1641 1642 1643 class FastPackedSmiElementsAccessor 1644 : public FastSmiOrObjectElementsAccessor< 1645 FastPackedSmiElementsAccessor, 1646 ElementsKindTraits<FAST_SMI_ELEMENTS> > { 1647 public: 1648 explicit FastPackedSmiElementsAccessor(const char* name) 1649 : FastSmiOrObjectElementsAccessor< 1650 FastPackedSmiElementsAccessor, 1651 ElementsKindTraits<FAST_SMI_ELEMENTS> >(name) {} 1652 }; 1653 1654 1655 class FastHoleySmiElementsAccessor 1656 : public FastSmiOrObjectElementsAccessor< 1657 FastHoleySmiElementsAccessor, 1658 ElementsKindTraits<FAST_HOLEY_SMI_ELEMENTS> > { 1659 public: 1660 explicit FastHoleySmiElementsAccessor(const char* name) 1661 : FastSmiOrObjectElementsAccessor< 1662 FastHoleySmiElementsAccessor, 1663 ElementsKindTraits<FAST_HOLEY_SMI_ELEMENTS> >(name) {} 1664 }; 1665 1666 1667 class FastPackedObjectElementsAccessor 1668 : public FastSmiOrObjectElementsAccessor< 1669 FastPackedObjectElementsAccessor, 1670 ElementsKindTraits<FAST_ELEMENTS> > { 1671 public: 1672 explicit FastPackedObjectElementsAccessor(const char* name) 1673 : FastSmiOrObjectElementsAccessor< 1674 FastPackedObjectElementsAccessor, 1675 ElementsKindTraits<FAST_ELEMENTS> >(name) {} 1676 }; 1677 1678 1679 class FastHoleyObjectElementsAccessor 1680 : public FastSmiOrObjectElementsAccessor< 1681 FastHoleyObjectElementsAccessor, 1682 ElementsKindTraits<FAST_HOLEY_ELEMENTS> > { 1683 public: 1684 explicit FastHoleyObjectElementsAccessor(const char* name) 1685 : FastSmiOrObjectElementsAccessor< 1686 FastHoleyObjectElementsAccessor, 1687 ElementsKindTraits<FAST_HOLEY_ELEMENTS> >(name) {} 1688 }; 1689 1690 1691 template<typename FastElementsAccessorSubclass, 1692 typename KindTraits> 1693 class FastDoubleElementsAccessor 1694 : public FastElementsAccessor<FastElementsAccessorSubclass, KindTraits> { 1695 public: 1696 explicit FastDoubleElementsAccessor(const char* name) 1697 : FastElementsAccessor<FastElementsAccessorSubclass, 1698 KindTraits>(name) {} 1699 1700 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 1701 Object* value) { 1702 FixedDoubleArray::cast(backing_store)->set(entry, value->Number()); 1703 } 1704 1705 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 1706 Object* value, WriteBarrierMode mode) { 1707 FixedDoubleArray::cast(backing_store)->set(entry, value->Number()); 1708 } 1709 1710 static void MoveElements(Isolate* isolate, Handle<JSArray> receiver, 1711 Handle<FixedArrayBase> backing_store, int dst_index, 1712 int src_index, int len, int hole_start, 1713 int hole_end) { 1714 Heap* heap = isolate->heap(); 1715 Handle<FixedDoubleArray> dst_elms = 1716 Handle<FixedDoubleArray>::cast(backing_store); 1717 if (heap->CanMoveObjectStart(*dst_elms) && dst_index == 0) { 1718 // Update all the copies of this backing_store handle. 1719 *dst_elms.location() = FixedDoubleArray::cast( 1720 heap->LeftTrimFixedArray(*dst_elms, src_index)); 1721 receiver->set_elements(*dst_elms); 1722 // Adjust the hole offset as the array has been shrunk. 1723 hole_end -= src_index; 1724 DCHECK_LE(hole_start, backing_store->length()); 1725 DCHECK_LE(hole_end, backing_store->length()); 1726 } else if (len != 0) { 1727 MemMove(dst_elms->data_start() + dst_index, 1728 dst_elms->data_start() + src_index, len * kDoubleSize); 1729 } 1730 if (hole_start != hole_end) { 1731 dst_elms->FillWithHoles(hole_start, hole_end); 1732 } 1733 } 1734 1735 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 1736 FixedArrayBase* to, ElementsKind from_kind, 1737 uint32_t to_start, int packed_size, 1738 int copy_size) { 1739 DisallowHeapAllocation no_allocation; 1740 switch (from_kind) { 1741 case FAST_SMI_ELEMENTS: 1742 CopyPackedSmiToDoubleElements(from, from_start, to, to_start, 1743 packed_size, copy_size); 1744 break; 1745 case FAST_HOLEY_SMI_ELEMENTS: 1746 CopySmiToDoubleElements(from, from_start, to, to_start, copy_size); 1747 break; 1748 case FAST_DOUBLE_ELEMENTS: 1749 case FAST_HOLEY_DOUBLE_ELEMENTS: 1750 CopyDoubleToDoubleElements(from, from_start, to, to_start, copy_size); 1751 break; 1752 case FAST_ELEMENTS: 1753 case FAST_HOLEY_ELEMENTS: 1754 CopyObjectToDoubleElements(from, from_start, to, to_start, copy_size); 1755 break; 1756 case DICTIONARY_ELEMENTS: 1757 CopyDictionaryToDoubleElements(from, from_start, to, to_start, 1758 copy_size); 1759 break; 1760 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: 1761 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: 1762 UNREACHABLE(); 1763 1764 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ 1765 case TYPE##_ELEMENTS: \ 1766 UNREACHABLE(); 1767 TYPED_ARRAYS(TYPED_ARRAY_CASE) 1768 #undef TYPED_ARRAY_CASE 1769 } 1770 } 1771 }; 1772 1773 1774 class FastPackedDoubleElementsAccessor 1775 : public FastDoubleElementsAccessor< 1776 FastPackedDoubleElementsAccessor, 1777 ElementsKindTraits<FAST_DOUBLE_ELEMENTS> > { 1778 public: 1779 explicit FastPackedDoubleElementsAccessor(const char* name) 1780 : FastDoubleElementsAccessor< 1781 FastPackedDoubleElementsAccessor, 1782 ElementsKindTraits<FAST_DOUBLE_ELEMENTS> >(name) {} 1783 }; 1784 1785 1786 class FastHoleyDoubleElementsAccessor 1787 : public FastDoubleElementsAccessor< 1788 FastHoleyDoubleElementsAccessor, 1789 ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> > { 1790 public: 1791 explicit FastHoleyDoubleElementsAccessor(const char* name) 1792 : FastDoubleElementsAccessor< 1793 FastHoleyDoubleElementsAccessor, 1794 ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> >(name) {} 1795 }; 1796 1797 1798 // Super class for all external element arrays. 1799 template<ElementsKind Kind> 1800 class TypedElementsAccessor 1801 : public ElementsAccessorBase<TypedElementsAccessor<Kind>, 1802 ElementsKindTraits<Kind> > { 1803 public: 1804 explicit TypedElementsAccessor(const char* name) 1805 : ElementsAccessorBase<AccessorClass, 1806 ElementsKindTraits<Kind> >(name) {} 1807 1808 typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore; 1809 typedef TypedElementsAccessor<Kind> AccessorClass; 1810 1811 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 1812 Object* value) { 1813 BackingStore::cast(backing_store)->SetValue(entry, value); 1814 } 1815 1816 static inline void SetImpl(FixedArrayBase* backing_store, uint32_t entry, 1817 Object* value, WriteBarrierMode mode) { 1818 BackingStore::cast(backing_store)->SetValue(entry, value); 1819 } 1820 1821 static Handle<Object> GetImpl(Handle<FixedArrayBase> backing_store, 1822 uint32_t entry) { 1823 uint32_t index = GetIndexForEntryImpl(*backing_store, entry); 1824 return BackingStore::get(Handle<BackingStore>::cast(backing_store), index); 1825 } 1826 1827 static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, 1828 uint32_t entry) { 1829 return PropertyDetails(DONT_DELETE, DATA, 0, PropertyCellType::kNoCell); 1830 } 1831 1832 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array, 1833 uint32_t length, 1834 Handle<FixedArrayBase> backing_store) { 1835 // External arrays do not support changing their length. 1836 UNREACHABLE(); 1837 } 1838 1839 static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) { 1840 UNREACHABLE(); 1841 } 1842 1843 static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store, 1844 uint32_t entry) { 1845 return entry; 1846 } 1847 1848 static uint32_t GetEntryForIndexImpl(JSObject* holder, 1849 FixedArrayBase* backing_store, 1850 uint32_t index, PropertyFilter filter) { 1851 return index < AccessorClass::GetCapacityImpl(holder, backing_store) 1852 ? index 1853 : kMaxUInt32; 1854 } 1855 1856 static uint32_t GetCapacityImpl(JSObject* holder, 1857 FixedArrayBase* backing_store) { 1858 JSArrayBufferView* view = JSArrayBufferView::cast(holder); 1859 if (view->WasNeutered()) return 0; 1860 return backing_store->length(); 1861 } 1862 }; 1863 1864 1865 1866 #define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype, size) \ 1867 typedef TypedElementsAccessor<TYPE##_ELEMENTS > \ 1868 Fixed##Type##ElementsAccessor; 1869 1870 TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR) 1871 #undef FIXED_ELEMENTS_ACCESSOR 1872 1873 1874 template <typename SloppyArgumentsElementsAccessorSubclass, 1875 typename ArgumentsAccessor, typename KindTraits> 1876 class SloppyArgumentsElementsAccessor 1877 : public ElementsAccessorBase<SloppyArgumentsElementsAccessorSubclass, 1878 KindTraits> { 1879 public: 1880 explicit SloppyArgumentsElementsAccessor(const char* name) 1881 : ElementsAccessorBase<SloppyArgumentsElementsAccessorSubclass, 1882 KindTraits>(name) { 1883 USE(KindTraits::Kind); 1884 } 1885 1886 static Handle<Object> GetImpl(Handle<FixedArrayBase> parameters, 1887 uint32_t entry) { 1888 Isolate* isolate = parameters->GetIsolate(); 1889 Handle<FixedArray> parameter_map = Handle<FixedArray>::cast(parameters); 1890 uint32_t length = parameter_map->length() - 2; 1891 if (entry < length) { 1892 DisallowHeapAllocation no_gc; 1893 Object* probe = parameter_map->get(entry + 2); 1894 Context* context = Context::cast(parameter_map->get(0)); 1895 int context_entry = Smi::cast(probe)->value(); 1896 DCHECK(!context->get(context_entry)->IsTheHole()); 1897 return handle(context->get(context_entry), isolate); 1898 } else { 1899 // Object is not mapped, defer to the arguments. 1900 Handle<FixedArray> arguments(FixedArray::cast(parameter_map->get(1)), 1901 isolate); 1902 Handle<Object> result = 1903 ArgumentsAccessor::GetImpl(arguments, entry - length); 1904 // Elements of the arguments object in slow mode might be slow aliases. 1905 if (result->IsAliasedArgumentsEntry()) { 1906 DisallowHeapAllocation no_gc; 1907 AliasedArgumentsEntry* alias = AliasedArgumentsEntry::cast(*result); 1908 Context* context = Context::cast(parameter_map->get(0)); 1909 int context_entry = alias->aliased_context_slot(); 1910 DCHECK(!context->get(context_entry)->IsTheHole()); 1911 return handle(context->get(context_entry), isolate); 1912 } 1913 return result; 1914 } 1915 } 1916 1917 static void GrowCapacityAndConvertImpl(Handle<JSObject> object, 1918 uint32_t capacity) { 1919 UNREACHABLE(); 1920 } 1921 1922 static inline void SetImpl(FixedArrayBase* store, uint32_t entry, 1923 Object* value) { 1924 FixedArray* parameter_map = FixedArray::cast(store); 1925 uint32_t length = parameter_map->length() - 2; 1926 if (entry < length) { 1927 Object* probe = parameter_map->get(entry + 2); 1928 Context* context = Context::cast(parameter_map->get(0)); 1929 int context_entry = Smi::cast(probe)->value(); 1930 DCHECK(!context->get(context_entry)->IsTheHole()); 1931 context->set(context_entry, value); 1932 } else { 1933 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 1934 Object* current = ArgumentsAccessor::GetRaw(arguments, entry - length); 1935 if (current->IsAliasedArgumentsEntry()) { 1936 AliasedArgumentsEntry* alias = AliasedArgumentsEntry::cast(current); 1937 Context* context = Context::cast(parameter_map->get(0)); 1938 int context_entry = alias->aliased_context_slot(); 1939 DCHECK(!context->get(context_entry)->IsTheHole()); 1940 context->set(context_entry, value); 1941 } else { 1942 ArgumentsAccessor::SetImpl(arguments, entry - length, value); 1943 } 1944 } 1945 } 1946 1947 static void SetLengthImpl(Isolate* isolate, Handle<JSArray> array, 1948 uint32_t length, 1949 Handle<FixedArrayBase> parameter_map) { 1950 // Sloppy arguments objects are not arrays. 1951 UNREACHABLE(); 1952 } 1953 1954 static uint32_t GetCapacityImpl(JSObject* holder, 1955 FixedArrayBase* backing_store) { 1956 FixedArray* parameter_map = FixedArray::cast(backing_store); 1957 FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); 1958 return parameter_map->length() - 2 + 1959 ArgumentsAccessor::GetCapacityImpl(holder, arguments); 1960 } 1961 1962 static bool HasEntryImpl(FixedArrayBase* parameters, uint32_t entry) { 1963 FixedArray* parameter_map = FixedArray::cast(parameters); 1964 uint32_t length = parameter_map->length() - 2; 1965 if (entry < length) { 1966 return !GetParameterMapArg(parameter_map, entry)->IsTheHole(); 1967 } 1968 1969 FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); 1970 return ArgumentsAccessor::HasEntryImpl(arguments, entry - length); 1971 } 1972 1973 static uint32_t GetIndexForEntryImpl(FixedArrayBase* parameters, 1974 uint32_t entry) { 1975 FixedArray* parameter_map = FixedArray::cast(parameters); 1976 uint32_t length = parameter_map->length() - 2; 1977 if (entry < length) return entry; 1978 1979 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 1980 return ArgumentsAccessor::GetIndexForEntryImpl(arguments, entry - length); 1981 } 1982 1983 static uint32_t GetEntryForIndexImpl(JSObject* holder, 1984 FixedArrayBase* parameters, 1985 uint32_t index, PropertyFilter filter) { 1986 FixedArray* parameter_map = FixedArray::cast(parameters); 1987 Object* probe = GetParameterMapArg(parameter_map, index); 1988 if (!probe->IsTheHole()) return index; 1989 1990 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 1991 uint32_t entry = ArgumentsAccessor::GetEntryForIndexImpl(holder, arguments, 1992 index, filter); 1993 if (entry == kMaxUInt32) return entry; 1994 return (parameter_map->length() - 2) + entry; 1995 } 1996 1997 static PropertyDetails GetDetailsImpl(FixedArrayBase* parameters, 1998 uint32_t entry) { 1999 FixedArray* parameter_map = FixedArray::cast(parameters); 2000 uint32_t length = parameter_map->length() - 2; 2001 if (entry < length) { 2002 return PropertyDetails(NONE, DATA, 0, PropertyCellType::kNoCell); 2003 } 2004 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 2005 return ArgumentsAccessor::GetDetailsImpl(arguments, entry - length); 2006 } 2007 2008 static Object* GetParameterMapArg(FixedArray* parameter_map, uint32_t index) { 2009 uint32_t length = parameter_map->length() - 2; 2010 return index < length 2011 ? parameter_map->get(index + 2) 2012 : Object::cast(parameter_map->GetHeap()->the_hole_value()); 2013 } 2014 2015 static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) { 2016 FixedArray* parameter_map = FixedArray::cast(obj->elements()); 2017 uint32_t length = static_cast<uint32_t>(parameter_map->length()) - 2; 2018 if (entry < length) { 2019 // TODO(kmillikin): We could check if this was the last aliased 2020 // parameter, and revert to normal elements in that case. That 2021 // would enable GC of the context. 2022 parameter_map->set_the_hole(entry + 2); 2023 } else { 2024 SloppyArgumentsElementsAccessorSubclass::DeleteFromArguments( 2025 obj, entry - length); 2026 } 2027 } 2028 }; 2029 2030 2031 class SlowSloppyArgumentsElementsAccessor 2032 : public SloppyArgumentsElementsAccessor< 2033 SlowSloppyArgumentsElementsAccessor, DictionaryElementsAccessor, 2034 ElementsKindTraits<SLOW_SLOPPY_ARGUMENTS_ELEMENTS> > { 2035 public: 2036 explicit SlowSloppyArgumentsElementsAccessor(const char* name) 2037 : SloppyArgumentsElementsAccessor< 2038 SlowSloppyArgumentsElementsAccessor, DictionaryElementsAccessor, 2039 ElementsKindTraits<SLOW_SLOPPY_ARGUMENTS_ELEMENTS> >(name) {} 2040 2041 static void DeleteFromArguments(Handle<JSObject> obj, uint32_t entry) { 2042 Handle<FixedArray> parameter_map(FixedArray::cast(obj->elements())); 2043 Handle<SeededNumberDictionary> dict( 2044 SeededNumberDictionary::cast(parameter_map->get(1))); 2045 // TODO(verwaest): Remove reliance on index in Shrink. 2046 uint32_t index = GetIndexForEntryImpl(*dict, entry); 2047 Handle<Object> result = SeededNumberDictionary::DeleteProperty(dict, entry); 2048 USE(result); 2049 DCHECK(result->IsTrue()); 2050 Handle<FixedArray> new_elements = 2051 SeededNumberDictionary::Shrink(dict, index); 2052 parameter_map->set(1, *new_elements); 2053 } 2054 2055 static void AddImpl(Handle<JSObject> object, uint32_t index, 2056 Handle<Object> value, PropertyAttributes attributes, 2057 uint32_t new_capacity) { 2058 Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); 2059 Handle<FixedArrayBase> old_elements( 2060 FixedArrayBase::cast(parameter_map->get(1))); 2061 Handle<SeededNumberDictionary> dictionary = 2062 old_elements->IsSeededNumberDictionary() 2063 ? Handle<SeededNumberDictionary>::cast(old_elements) 2064 : JSObject::NormalizeElements(object); 2065 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); 2066 Handle<SeededNumberDictionary> new_dictionary = 2067 SeededNumberDictionary::AddNumberEntry( 2068 dictionary, index, value, details, 2069 object->map()->is_prototype_map()); 2070 if (attributes != NONE) object->RequireSlowElements(*new_dictionary); 2071 if (*dictionary != *new_dictionary) { 2072 FixedArray::cast(object->elements())->set(1, *new_dictionary); 2073 } 2074 } 2075 2076 static void ReconfigureImpl(Handle<JSObject> object, 2077 Handle<FixedArrayBase> store, uint32_t entry, 2078 Handle<Object> value, 2079 PropertyAttributes attributes) { 2080 Handle<FixedArray> parameter_map = Handle<FixedArray>::cast(store); 2081 uint32_t length = parameter_map->length() - 2; 2082 if (entry < length) { 2083 Object* probe = parameter_map->get(entry + 2); 2084 DCHECK(!probe->IsTheHole()); 2085 Context* context = Context::cast(parameter_map->get(0)); 2086 int context_entry = Smi::cast(probe)->value(); 2087 DCHECK(!context->get(context_entry)->IsTheHole()); 2088 context->set(context_entry, *value); 2089 2090 // Redefining attributes of an aliased element destroys fast aliasing. 2091 parameter_map->set_the_hole(entry + 2); 2092 // For elements that are still writable we re-establish slow aliasing. 2093 if ((attributes & READ_ONLY) == 0) { 2094 Isolate* isolate = store->GetIsolate(); 2095 value = isolate->factory()->NewAliasedArgumentsEntry(context_entry); 2096 } 2097 2098 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); 2099 Handle<SeededNumberDictionary> arguments( 2100 SeededNumberDictionary::cast(parameter_map->get(1))); 2101 arguments = SeededNumberDictionary::AddNumberEntry( 2102 arguments, entry, value, details, object->map()->is_prototype_map()); 2103 // If the attributes were NONE, we would have called set rather than 2104 // reconfigure. 2105 DCHECK_NE(NONE, attributes); 2106 object->RequireSlowElements(*arguments); 2107 parameter_map->set(1, *arguments); 2108 } else { 2109 Handle<FixedArrayBase> arguments( 2110 FixedArrayBase::cast(parameter_map->get(1))); 2111 DictionaryElementsAccessor::ReconfigureImpl( 2112 object, arguments, entry - length, value, attributes); 2113 } 2114 } 2115 }; 2116 2117 2118 class FastSloppyArgumentsElementsAccessor 2119 : public SloppyArgumentsElementsAccessor< 2120 FastSloppyArgumentsElementsAccessor, FastHoleyObjectElementsAccessor, 2121 ElementsKindTraits<FAST_SLOPPY_ARGUMENTS_ELEMENTS> > { 2122 public: 2123 explicit FastSloppyArgumentsElementsAccessor(const char* name) 2124 : SloppyArgumentsElementsAccessor< 2125 FastSloppyArgumentsElementsAccessor, 2126 FastHoleyObjectElementsAccessor, 2127 ElementsKindTraits<FAST_SLOPPY_ARGUMENTS_ELEMENTS> >(name) {} 2128 2129 static void DeleteFromArguments(Handle<JSObject> obj, uint32_t entry) { 2130 FixedArray* parameter_map = FixedArray::cast(obj->elements()); 2131 Handle<FixedArray> arguments(FixedArray::cast(parameter_map->get(1))); 2132 FastHoleyObjectElementsAccessor::DeleteCommon(obj, entry, arguments); 2133 } 2134 2135 static void AddImpl(Handle<JSObject> object, uint32_t index, 2136 Handle<Object> value, PropertyAttributes attributes, 2137 uint32_t new_capacity) { 2138 DCHECK_EQ(NONE, attributes); 2139 Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); 2140 Handle<FixedArrayBase> old_elements( 2141 FixedArrayBase::cast(parameter_map->get(1))); 2142 if (old_elements->IsSeededNumberDictionary() || 2143 static_cast<uint32_t>(old_elements->length()) < new_capacity) { 2144 GrowCapacityAndConvertImpl(object, new_capacity); 2145 } 2146 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 2147 // For fast holey objects, the entry equals the index. The code above made 2148 // sure that there's enough space to store the value. We cannot convert 2149 // index to entry explicitly since the slot still contains the hole, so the 2150 // current EntryForIndex would indicate that it is "absent" by returning 2151 // kMaxUInt32. 2152 FastHoleyObjectElementsAccessor::SetImpl(arguments, index, *value); 2153 } 2154 2155 static void ReconfigureImpl(Handle<JSObject> object, 2156 Handle<FixedArrayBase> store, uint32_t entry, 2157 Handle<Object> value, 2158 PropertyAttributes attributes) { 2159 Handle<SeededNumberDictionary> dictionary = 2160 JSObject::NormalizeElements(object); 2161 FixedArray::cast(*store)->set(1, *dictionary); 2162 uint32_t length = static_cast<uint32_t>(store->length()) - 2; 2163 if (entry >= length) { 2164 entry = dictionary->FindEntry(entry - length) + length; 2165 } 2166 SlowSloppyArgumentsElementsAccessor::ReconfigureImpl(object, store, entry, 2167 value, attributes); 2168 } 2169 2170 static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start, 2171 FixedArrayBase* to, ElementsKind from_kind, 2172 uint32_t to_start, int packed_size, 2173 int copy_size) { 2174 DCHECK(!to->IsDictionary()); 2175 if (from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS) { 2176 CopyDictionaryToObjectElements(from, from_start, to, FAST_HOLEY_ELEMENTS, 2177 to_start, copy_size); 2178 } else { 2179 DCHECK_EQ(FAST_SLOPPY_ARGUMENTS_ELEMENTS, from_kind); 2180 CopyObjectToObjectElements(from, FAST_HOLEY_ELEMENTS, from_start, to, 2181 FAST_HOLEY_ELEMENTS, to_start, copy_size); 2182 } 2183 } 2184 2185 static void GrowCapacityAndConvertImpl(Handle<JSObject> object, 2186 uint32_t capacity) { 2187 Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); 2188 Handle<FixedArray> old_elements(FixedArray::cast(parameter_map->get(1))); 2189 ElementsKind from_kind = object->GetElementsKind(); 2190 // This method should only be called if there's a reason to update the 2191 // elements. 2192 DCHECK(from_kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS || 2193 static_cast<uint32_t>(old_elements->length()) < capacity); 2194 Handle<FixedArrayBase> elements = 2195 ConvertElementsWithCapacity(object, old_elements, from_kind, capacity); 2196 Handle<Map> new_map = JSObject::GetElementsTransitionMap( 2197 object, FAST_SLOPPY_ARGUMENTS_ELEMENTS); 2198 JSObject::MigrateToMap(object, new_map); 2199 parameter_map->set(1, *elements); 2200 JSObject::ValidateElements(object); 2201 } 2202 }; 2203 2204 2205 } // namespace 2206 2207 2208 void CheckArrayAbuse(Handle<JSObject> obj, const char* op, uint32_t index, 2209 bool allow_appending) { 2210 DisallowHeapAllocation no_allocation; 2211 Object* raw_length = NULL; 2212 const char* elements_type = "array"; 2213 if (obj->IsJSArray()) { 2214 JSArray* array = JSArray::cast(*obj); 2215 raw_length = array->length(); 2216 } else { 2217 raw_length = Smi::FromInt(obj->elements()->length()); 2218 elements_type = "object"; 2219 } 2220 2221 if (raw_length->IsNumber()) { 2222 double n = raw_length->Number(); 2223 if (FastI2D(FastD2UI(n)) == n) { 2224 int32_t int32_length = DoubleToInt32(n); 2225 uint32_t compare_length = static_cast<uint32_t>(int32_length); 2226 if (allow_appending) compare_length++; 2227 if (index >= compare_length) { 2228 PrintF("[OOB %s %s (%s length = %d, element accessed = %d) in ", 2229 elements_type, op, elements_type, static_cast<int>(int32_length), 2230 static_cast<int>(index)); 2231 TraceTopFrame(obj->GetIsolate()); 2232 PrintF("]\n"); 2233 } 2234 } else { 2235 PrintF("[%s elements length not integer value in ", elements_type); 2236 TraceTopFrame(obj->GetIsolate()); 2237 PrintF("]\n"); 2238 } 2239 } else { 2240 PrintF("[%s elements length not a number in ", elements_type); 2241 TraceTopFrame(obj->GetIsolate()); 2242 PrintF("]\n"); 2243 } 2244 } 2245 2246 2247 MaybeHandle<Object> ArrayConstructInitializeElements(Handle<JSArray> array, 2248 Arguments* args) { 2249 if (args->length() == 0) { 2250 // Optimize the case where there are no parameters passed. 2251 JSArray::Initialize(array, JSArray::kPreallocatedArrayElements); 2252 return array; 2253 2254 } else if (args->length() == 1 && args->at<Object>(0)->IsNumber()) { 2255 uint32_t length; 2256 if (!args->at<Object>(0)->ToArrayLength(&length)) { 2257 return ThrowArrayLengthRangeError(array->GetIsolate()); 2258 } 2259 2260 // Optimize the case where there is one argument and the argument is a small 2261 // smi. 2262 if (length > 0 && length < JSArray::kInitialMaxFastElementArray) { 2263 ElementsKind elements_kind = array->GetElementsKind(); 2264 JSArray::Initialize(array, length, length); 2265 2266 if (!IsFastHoleyElementsKind(elements_kind)) { 2267 elements_kind = GetHoleyElementsKind(elements_kind); 2268 JSObject::TransitionElementsKind(array, elements_kind); 2269 } 2270 } else if (length == 0) { 2271 JSArray::Initialize(array, JSArray::kPreallocatedArrayElements); 2272 } else { 2273 // Take the argument as the length. 2274 JSArray::Initialize(array, 0); 2275 JSArray::SetLength(array, length); 2276 } 2277 return array; 2278 } 2279 2280 Factory* factory = array->GetIsolate()->factory(); 2281 2282 // Set length and elements on the array. 2283 int number_of_elements = args->length(); 2284 JSObject::EnsureCanContainElements( 2285 array, args, 0, number_of_elements, ALLOW_CONVERTED_DOUBLE_ELEMENTS); 2286 2287 // Allocate an appropriately typed elements array. 2288 ElementsKind elements_kind = array->GetElementsKind(); 2289 Handle<FixedArrayBase> elms; 2290 if (IsFastDoubleElementsKind(elements_kind)) { 2291 elms = Handle<FixedArrayBase>::cast( 2292 factory->NewFixedDoubleArray(number_of_elements)); 2293 } else { 2294 elms = Handle<FixedArrayBase>::cast( 2295 factory->NewFixedArrayWithHoles(number_of_elements)); 2296 } 2297 2298 // Fill in the content 2299 switch (array->GetElementsKind()) { 2300 case FAST_HOLEY_SMI_ELEMENTS: 2301 case FAST_SMI_ELEMENTS: { 2302 Handle<FixedArray> smi_elms = Handle<FixedArray>::cast(elms); 2303 for (int entry = 0; entry < number_of_elements; entry++) { 2304 smi_elms->set(entry, (*args)[entry], SKIP_WRITE_BARRIER); 2305 } 2306 break; 2307 } 2308 case FAST_HOLEY_ELEMENTS: 2309 case FAST_ELEMENTS: { 2310 DisallowHeapAllocation no_gc; 2311 WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); 2312 Handle<FixedArray> object_elms = Handle<FixedArray>::cast(elms); 2313 for (int entry = 0; entry < number_of_elements; entry++) { 2314 object_elms->set(entry, (*args)[entry], mode); 2315 } 2316 break; 2317 } 2318 case FAST_HOLEY_DOUBLE_ELEMENTS: 2319 case FAST_DOUBLE_ELEMENTS: { 2320 Handle<FixedDoubleArray> double_elms = 2321 Handle<FixedDoubleArray>::cast(elms); 2322 for (int entry = 0; entry < number_of_elements; entry++) { 2323 double_elms->set(entry, (*args)[entry]->Number()); 2324 } 2325 break; 2326 } 2327 default: 2328 UNREACHABLE(); 2329 break; 2330 } 2331 2332 array->set_elements(*elms); 2333 array->set_length(Smi::FromInt(number_of_elements)); 2334 return array; 2335 } 2336 2337 2338 void ElementsAccessor::InitializeOncePerProcess() { 2339 static ElementsAccessor* accessor_array[] = { 2340 #define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind), 2341 ELEMENTS_LIST(ACCESSOR_ARRAY) 2342 #undef ACCESSOR_ARRAY 2343 }; 2344 2345 STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) == 2346 kElementsKindCount); 2347 2348 elements_accessors_ = accessor_array; 2349 } 2350 2351 2352 void ElementsAccessor::TearDown() { 2353 if (elements_accessors_ == NULL) return; 2354 #define ACCESSOR_DELETE(Class, Kind, Store) delete elements_accessors_[Kind]; 2355 ELEMENTS_LIST(ACCESSOR_DELETE) 2356 #undef ACCESSOR_DELETE 2357 elements_accessors_ = NULL; 2358 } 2359 2360 2361 Handle<JSArray> ElementsAccessor::Concat(Isolate* isolate, Arguments* args, 2362 uint32_t concat_size) { 2363 int result_len = 0; 2364 ElementsKind elements_kind = GetInitialFastElementsKind(); 2365 bool has_double = false; 2366 { 2367 DisallowHeapAllocation no_gc; 2368 // Iterate through all the arguments performing checks 2369 // and calculating total length. 2370 bool is_holey = false; 2371 for (uint32_t i = 0; i < concat_size; i++) { 2372 Object* arg = (*args)[i]; 2373 int len = Smi::cast(JSArray::cast(arg)->length())->value(); 2374 2375 // We shouldn't overflow when adding another len. 2376 const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2); 2377 STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt); 2378 USE(kHalfOfMaxInt); 2379 result_len += len; 2380 DCHECK(0 <= result_len); 2381 DCHECK(result_len <= FixedDoubleArray::kMaxLength); 2382 2383 ElementsKind arg_kind = JSArray::cast(arg)->map()->elements_kind(); 2384 has_double = has_double || IsFastDoubleElementsKind(arg_kind); 2385 is_holey = is_holey || IsFastHoleyElementsKind(arg_kind); 2386 elements_kind = GetMoreGeneralElementsKind(elements_kind, arg_kind); 2387 } 2388 if (is_holey) { 2389 elements_kind = GetHoleyElementsKind(elements_kind); 2390 } 2391 } 2392 2393 // If a double array is concatted into a fast elements array, the fast 2394 // elements array needs to be initialized to contain proper holes, since 2395 // boxing doubles may cause incremental marking. 2396 ArrayStorageAllocationMode mode = 2397 has_double && IsFastObjectElementsKind(elements_kind) 2398 ? INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE 2399 : DONT_INITIALIZE_ARRAY_ELEMENTS; 2400 Handle<JSArray> result_array = isolate->factory()->NewJSArray( 2401 elements_kind, result_len, result_len, Strength::WEAK, mode); 2402 if (result_len == 0) return result_array; 2403 int j = 0; 2404 Handle<FixedArrayBase> storage(result_array->elements(), isolate); 2405 ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); 2406 for (uint32_t i = 0; i < concat_size; i++) { 2407 // It is crucial to keep |array| in a raw pointer form to avoid 2408 // performance degradation. 2409 JSArray* array = JSArray::cast((*args)[i]); 2410 int len = Smi::cast(array->length())->value(); 2411 if (len > 0) { 2412 ElementsKind from_kind = array->GetElementsKind(); 2413 accessor->CopyElements(array, 0, from_kind, storage, j, len); 2414 j += len; 2415 } 2416 } 2417 2418 DCHECK(j == result_len); 2419 return result_array; 2420 } 2421 2422 ElementsAccessor** ElementsAccessor::elements_accessors_ = NULL; 2423 } // namespace internal 2424 } // namespace v8 2425