OLD | NEW |
---|---|
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/runtime/runtime-utils.h" | 5 #include "src/runtime/runtime-utils.h" |
6 | 6 |
7 #include "src/arguments.h" | 7 #include "src/arguments.h" |
8 #include "src/conversions-inl.h" | 8 #include "src/conversions-inl.h" |
9 #include "src/elements.h" | 9 #include "src/elements.h" |
10 #include "src/factory.h" | 10 #include "src/factory.h" |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
104 } | 104 } |
105 | 105 |
106 // Strict not needed. Used for cycle detection in Array join implementation. | 106 // Strict not needed. Used for cycle detection in Array join implementation. |
107 RETURN_FAILURE_ON_EXCEPTION( | 107 RETURN_FAILURE_ON_EXCEPTION( |
108 isolate, JSObject::AddDataElement(array, length, element, NONE)); | 108 isolate, JSObject::AddDataElement(array, length, element, NONE)); |
109 JSObject::ValidateElements(array); | 109 JSObject::ValidateElements(array); |
110 return isolate->heap()->true_value(); | 110 return isolate->heap()->true_value(); |
111 } | 111 } |
112 | 112 |
113 | 113 |
114 /** | |
115 * A simple visitor visits every element of Array's. | |
116 * The backend storage can be a fixed array for fast elements case, | |
117 * or a dictionary for sparse array. Since Dictionary is a subtype | |
118 * of FixedArray, the class can be used by both fast and slow cases. | |
119 * The second parameter of the constructor, fast_elements, specifies | |
120 * whether the storage is a FixedArray or Dictionary. | |
121 * | |
122 * An index limit is used to deal with the situation that a result array | |
123 * length overflows 32-bit non-negative integer. | |
124 */ | |
125 class ArrayConcatVisitor { | |
126 public: | |
127 ArrayConcatVisitor(Isolate* isolate, Handle<FixedArray> storage, | |
128 bool fast_elements) | |
129 : isolate_(isolate), | |
130 storage_(Handle<FixedArray>::cast( | |
131 isolate->global_handles()->Create(*storage))), | |
132 index_offset_(0u), | |
133 bit_field_(FastElementsField::encode(fast_elements) | | |
134 ExceedsLimitField::encode(false)) {} | |
135 | |
136 ~ArrayConcatVisitor() { clear_storage(); } | |
137 | |
138 void visit(uint32_t i, Handle<Object> elm) { | |
139 if (i >= JSObject::kMaxElementCount - index_offset_) { | |
140 set_exceeds_array_limit(true); | |
141 return; | |
142 } | |
143 uint32_t index = index_offset_ + i; | |
144 | |
145 if (fast_elements()) { | |
146 if (index < static_cast<uint32_t>(storage_->length())) { | |
147 storage_->set(index, *elm); | |
148 return; | |
149 } | |
150 // Our initial estimate of length was foiled, possibly by | |
151 // getters on the arrays increasing the length of later arrays | |
152 // during iteration. | |
153 // This shouldn't happen in anything but pathological cases. | |
154 SetDictionaryMode(); | |
155 // Fall-through to dictionary mode. | |
156 } | |
157 DCHECK(!fast_elements()); | |
158 Handle<SeededNumberDictionary> dict( | |
159 SeededNumberDictionary::cast(*storage_)); | |
160 // The object holding this backing store has just been allocated, so | |
161 // it cannot yet be used as a prototype. | |
162 Handle<SeededNumberDictionary> result = | |
163 SeededNumberDictionary::AtNumberPut(dict, index, elm, false); | |
164 if (!result.is_identical_to(dict)) { | |
165 // Dictionary needed to grow. | |
166 clear_storage(); | |
167 set_storage(*result); | |
168 } | |
169 } | |
170 | |
171 void increase_index_offset(uint32_t delta) { | |
172 if (JSObject::kMaxElementCount - index_offset_ < delta) { | |
173 index_offset_ = JSObject::kMaxElementCount; | |
174 } else { | |
175 index_offset_ += delta; | |
176 } | |
177 // If the initial length estimate was off (see special case in visit()), | |
178 // but the array blowing the limit didn't contain elements beyond the | |
179 // provided-for index range, go to dictionary mode now. | |
180 if (fast_elements() && | |
181 index_offset_ > | |
182 static_cast<uint32_t>(FixedArrayBase::cast(*storage_)->length())) { | |
183 SetDictionaryMode(); | |
184 } | |
185 } | |
186 | |
187 bool exceeds_array_limit() const { | |
188 return ExceedsLimitField::decode(bit_field_); | |
189 } | |
190 | |
191 Handle<JSArray> ToArray() { | |
192 Handle<JSArray> array = isolate_->factory()->NewJSArray(0); | |
193 Handle<Object> length = | |
194 isolate_->factory()->NewNumber(static_cast<double>(index_offset_)); | |
195 Handle<Map> map = JSObject::GetElementsTransitionMap( | |
196 array, fast_elements() ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS); | |
197 array->set_map(*map); | |
198 array->set_length(*length); | |
199 array->set_elements(*storage_); | |
200 return array; | |
201 } | |
202 | |
203 private: | |
204 // Convert storage to dictionary mode. | |
205 void SetDictionaryMode() { | |
206 DCHECK(fast_elements()); | |
207 Handle<FixedArray> current_storage(*storage_); | |
208 Handle<SeededNumberDictionary> slow_storage( | |
209 SeededNumberDictionary::New(isolate_, current_storage->length())); | |
210 uint32_t current_length = static_cast<uint32_t>(current_storage->length()); | |
211 for (uint32_t i = 0; i < current_length; i++) { | |
212 HandleScope loop_scope(isolate_); | |
213 Handle<Object> element(current_storage->get(i), isolate_); | |
214 if (!element->IsTheHole()) { | |
215 // The object holding this backing store has just been allocated, so | |
216 // it cannot yet be used as a prototype. | |
217 Handle<SeededNumberDictionary> new_storage = | |
218 SeededNumberDictionary::AtNumberPut(slow_storage, i, element, | |
219 false); | |
220 if (!new_storage.is_identical_to(slow_storage)) { | |
221 slow_storage = loop_scope.CloseAndEscape(new_storage); | |
222 } | |
223 } | |
224 } | |
225 clear_storage(); | |
226 set_storage(*slow_storage); | |
227 set_fast_elements(false); | |
228 } | |
229 | |
230 inline void clear_storage() { | |
231 GlobalHandles::Destroy(Handle<Object>::cast(storage_).location()); | |
232 } | |
233 | |
234 inline void set_storage(FixedArray* storage) { | |
235 storage_ = | |
236 Handle<FixedArray>::cast(isolate_->global_handles()->Create(storage)); | |
237 } | |
238 | |
239 class FastElementsField : public BitField<bool, 0, 1> {}; | |
240 class ExceedsLimitField : public BitField<bool, 1, 1> {}; | |
241 | |
242 bool fast_elements() const { return FastElementsField::decode(bit_field_); } | |
243 void set_fast_elements(bool fast) { | |
244 bit_field_ = FastElementsField::update(bit_field_, fast); | |
245 } | |
246 void set_exceeds_array_limit(bool exceeds) { | |
247 bit_field_ = ExceedsLimitField::update(bit_field_, exceeds); | |
248 } | |
249 | |
250 Isolate* isolate_; | |
251 Handle<FixedArray> storage_; // Always a global handle. | |
252 // Index after last seen index. Always less than or equal to | |
253 // JSObject::kMaxElementCount. | |
254 uint32_t index_offset_; | |
255 uint32_t bit_field_; | |
256 }; | |
257 | |
258 | |
259 static uint32_t EstimateElementCount(Handle<JSArray> array) { | |
260 uint32_t length = static_cast<uint32_t>(array->length()->Number()); | |
261 int element_count = 0; | |
262 switch (array->GetElementsKind()) { | |
263 case FAST_SMI_ELEMENTS: | |
264 case FAST_HOLEY_SMI_ELEMENTS: | |
265 case FAST_ELEMENTS: | |
266 case FAST_HOLEY_ELEMENTS: { | |
267 // Fast elements can't have lengths that are not representable by | |
268 // a 32-bit signed integer. | |
269 DCHECK(static_cast<int32_t>(FixedArray::kMaxLength) >= 0); | |
270 int fast_length = static_cast<int>(length); | |
271 Handle<FixedArray> elements(FixedArray::cast(array->elements())); | |
272 for (int i = 0; i < fast_length; i++) { | |
273 if (!elements->get(i)->IsTheHole()) element_count++; | |
274 } | |
275 break; | |
276 } | |
277 case FAST_DOUBLE_ELEMENTS: | |
278 case FAST_HOLEY_DOUBLE_ELEMENTS: { | |
279 // Fast elements can't have lengths that are not representable by | |
280 // a 32-bit signed integer. | |
281 DCHECK(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0); | |
282 int fast_length = static_cast<int>(length); | |
283 if (array->elements()->IsFixedArray()) { | |
284 DCHECK(FixedArray::cast(array->elements())->length() == 0); | |
285 break; | |
286 } | |
287 Handle<FixedDoubleArray> elements( | |
288 FixedDoubleArray::cast(array->elements())); | |
289 for (int i = 0; i < fast_length; i++) { | |
290 if (!elements->is_the_hole(i)) element_count++; | |
291 } | |
292 break; | |
293 } | |
294 case DICTIONARY_ELEMENTS: { | |
295 Handle<SeededNumberDictionary> dictionary( | |
296 SeededNumberDictionary::cast(array->elements())); | |
297 int capacity = dictionary->Capacity(); | |
298 for (int i = 0; i < capacity; i++) { | |
299 Handle<Object> key(dictionary->KeyAt(i), array->GetIsolate()); | |
300 if (dictionary->IsKey(*key)) { | |
301 element_count++; | |
302 } | |
303 } | |
304 break; | |
305 } | |
306 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: | |
307 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: | |
308 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ | |
309 case TYPE##_ELEMENTS: | |
310 | |
311 TYPED_ARRAYS(TYPED_ARRAY_CASE) | |
312 #undef TYPED_ARRAY_CASE | |
313 // External arrays are always dense. | |
314 return length; | |
315 } | |
316 // As an estimate, we assume that the prototype doesn't contain any | |
317 // inherited elements. | |
318 return element_count; | |
319 } | |
320 | |
321 | |
322 template <class ExternalArrayClass, class ElementType> | |
323 static void IterateTypedArrayElements(Isolate* isolate, | |
324 Handle<JSObject> receiver, | |
325 bool elements_are_ints, | |
326 bool elements_are_guaranteed_smis, | |
327 ArrayConcatVisitor* visitor) { | |
328 Handle<ExternalArrayClass> array( | |
329 ExternalArrayClass::cast(receiver->elements())); | |
330 uint32_t len = static_cast<uint32_t>(array->length()); | |
331 | |
332 DCHECK(visitor != NULL); | |
333 if (elements_are_ints) { | |
334 if (elements_are_guaranteed_smis) { | |
335 for (uint32_t j = 0; j < len; j++) { | |
336 HandleScope loop_scope(isolate); | |
337 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get_scalar(j))), | |
338 isolate); | |
339 visitor->visit(j, e); | |
340 } | |
341 } else { | |
342 for (uint32_t j = 0; j < len; j++) { | |
343 HandleScope loop_scope(isolate); | |
344 int64_t val = static_cast<int64_t>(array->get_scalar(j)); | |
345 if (Smi::IsValid(static_cast<intptr_t>(val))) { | |
346 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)), isolate); | |
347 visitor->visit(j, e); | |
348 } else { | |
349 Handle<Object> e = | |
350 isolate->factory()->NewNumber(static_cast<ElementType>(val)); | |
351 visitor->visit(j, e); | |
352 } | |
353 } | |
354 } | |
355 } else { | |
356 for (uint32_t j = 0; j < len; j++) { | |
357 HandleScope loop_scope(isolate); | |
358 Handle<Object> e = isolate->factory()->NewNumber(array->get_scalar(j)); | |
359 visitor->visit(j, e); | |
360 } | |
361 } | |
362 } | |
363 | |
364 | |
365 // Used for sorting indices in a List<uint32_t>. | |
366 static int compareUInt32(const uint32_t* ap, const uint32_t* bp) { | |
367 uint32_t a = *ap; | |
368 uint32_t b = *bp; | |
369 return (a == b) ? 0 : (a < b) ? -1 : 1; | |
370 } | |
371 | |
372 | |
373 static void CollectElementIndices(Handle<JSObject> object, uint32_t range, | |
374 List<uint32_t>* indices) { | |
375 Isolate* isolate = object->GetIsolate(); | |
376 ElementsKind kind = object->GetElementsKind(); | |
377 switch (kind) { | |
378 case FAST_SMI_ELEMENTS: | |
379 case FAST_ELEMENTS: | |
380 case FAST_HOLEY_SMI_ELEMENTS: | |
381 case FAST_HOLEY_ELEMENTS: { | |
382 Handle<FixedArray> elements(FixedArray::cast(object->elements())); | |
383 uint32_t length = static_cast<uint32_t>(elements->length()); | |
384 if (range < length) length = range; | |
385 for (uint32_t i = 0; i < length; i++) { | |
386 if (!elements->get(i)->IsTheHole()) { | |
387 indices->Add(i); | |
388 } | |
389 } | |
390 break; | |
391 } | |
392 case FAST_HOLEY_DOUBLE_ELEMENTS: | |
393 case FAST_DOUBLE_ELEMENTS: { | |
394 if (object->elements()->IsFixedArray()) { | |
395 DCHECK(object->elements()->length() == 0); | |
396 break; | |
397 } | |
398 Handle<FixedDoubleArray> elements( | |
399 FixedDoubleArray::cast(object->elements())); | |
400 uint32_t length = static_cast<uint32_t>(elements->length()); | |
401 if (range < length) length = range; | |
402 for (uint32_t i = 0; i < length; i++) { | |
403 if (!elements->is_the_hole(i)) { | |
404 indices->Add(i); | |
405 } | |
406 } | |
407 break; | |
408 } | |
409 case DICTIONARY_ELEMENTS: { | |
410 Handle<SeededNumberDictionary> dict( | |
411 SeededNumberDictionary::cast(object->elements())); | |
412 uint32_t capacity = dict->Capacity(); | |
413 for (uint32_t j = 0; j < capacity; j++) { | |
414 HandleScope loop_scope(isolate); | |
415 Handle<Object> k(dict->KeyAt(j), isolate); | |
416 if (dict->IsKey(*k)) { | |
417 DCHECK(k->IsNumber()); | |
418 uint32_t index = static_cast<uint32_t>(k->Number()); | |
419 if (index < range) { | |
420 indices->Add(index); | |
421 } | |
422 } | |
423 } | |
424 break; | |
425 } | |
426 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ | |
427 case TYPE##_ELEMENTS: \ | |
428 | |
429 TYPED_ARRAYS(TYPED_ARRAY_CASE) | |
430 #undef TYPED_ARRAY_CASE | |
431 { | |
432 uint32_t length = static_cast<uint32_t>( | |
433 FixedArrayBase::cast(object->elements())->length()); | |
434 if (range <= length) { | |
435 length = range; | |
436 // We will add all indices, so we might as well clear it first | |
437 // and avoid duplicates. | |
438 indices->Clear(); | |
439 } | |
440 for (uint32_t i = 0; i < length; i++) { | |
441 indices->Add(i); | |
442 } | |
443 if (length == range) return; // All indices accounted for already. | |
444 break; | |
445 } | |
446 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: | |
447 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: { | |
448 ElementsAccessor* accessor = object->GetElementsAccessor(); | |
449 for (uint32_t i = 0; i < range; i++) { | |
450 if (accessor->HasElement(object, i)) { | |
451 indices->Add(i); | |
452 } | |
453 } | |
454 break; | |
455 } | |
456 } | |
457 | |
458 PrototypeIterator iter(isolate, object); | |
459 if (!iter.IsAtEnd()) { | |
460 // The prototype will usually have no inherited element indices, | |
461 // but we have to check. | |
462 CollectElementIndices( | |
463 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), range, | |
464 indices); | |
465 } | |
466 } | |
467 | |
468 | |
469 static bool IterateElementsSlow(Isolate* isolate, Handle<JSObject> receiver, | |
470 uint32_t length, ArrayConcatVisitor* visitor) { | |
471 for (uint32_t i = 0; i < length; ++i) { | |
472 HandleScope loop_scope(isolate); | |
473 Maybe<bool> maybe = JSReceiver::HasElement(receiver, i); | |
474 if (!maybe.IsJust()) return false; | |
475 if (maybe.FromJust()) { | |
476 Handle<Object> element_value; | |
477 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_value, | |
478 Object::GetElement(isolate, receiver, i), | |
479 false); | |
480 visitor->visit(i, element_value); | |
481 } | |
482 } | |
483 visitor->increase_index_offset(length); | |
484 return true; | |
485 } | |
486 | |
487 | |
488 /** | |
489 * A helper function that visits elements of a JSObject in numerical | |
490 * order. | |
491 * | |
492 * The visitor argument called for each existing element in the array | |
493 * with the element index and the element's value. | |
494 * Afterwards it increments the base-index of the visitor by the array | |
495 * length. | |
496 * Returns false if any access threw an exception, otherwise true. | |
497 */ | |
498 static bool IterateElements(Isolate* isolate, Handle<JSObject> receiver, | |
499 ArrayConcatVisitor* visitor) { | |
500 uint32_t length = 0; | |
501 | |
502 if (receiver->IsJSArray()) { | |
503 Handle<JSArray> array(Handle<JSArray>::cast(receiver)); | |
504 length = static_cast<uint32_t>(array->length()->Number()); | |
505 } else { | |
506 Handle<Object> val; | |
507 Handle<Object> key(isolate->heap()->length_string(), isolate); | |
508 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, val, | |
509 Runtime::GetObjectProperty(isolate, receiver, key), false); | |
510 // TODO(caitp): Support larger element indexes (up to 2^53-1). | |
511 if (!val->ToUint32(&length)) { | |
512 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, val, | |
513 Execution::ToLength(isolate, val), false); | |
514 val->ToUint32(&length); | |
515 } | |
516 } | |
517 | |
518 if (!(receiver->IsJSArray() || receiver->IsJSTypedArray())) { | |
519 // For classes which are not known to be safe to access via elements alone, | |
520 // use the slow case. | |
521 return IterateElementsSlow(isolate, receiver, length, visitor); | |
522 } | |
523 | |
524 switch (receiver->GetElementsKind()) { | |
525 case FAST_SMI_ELEMENTS: | |
526 case FAST_ELEMENTS: | |
527 case FAST_HOLEY_SMI_ELEMENTS: | |
528 case FAST_HOLEY_ELEMENTS: { | |
529 // Run through the elements FixedArray and use HasElement and GetElement | |
530 // to check the prototype for missing elements. | |
531 Handle<FixedArray> elements(FixedArray::cast(receiver->elements())); | |
532 int fast_length = static_cast<int>(length); | |
533 DCHECK(fast_length <= elements->length()); | |
534 for (int j = 0; j < fast_length; j++) { | |
535 HandleScope loop_scope(isolate); | |
536 Handle<Object> element_value(elements->get(j), isolate); | |
537 if (!element_value->IsTheHole()) { | |
538 visitor->visit(j, element_value); | |
539 } else { | |
540 Maybe<bool> maybe = JSReceiver::HasElement(receiver, j); | |
541 if (!maybe.IsJust()) return false; | |
542 if (maybe.FromJust()) { | |
543 // Call GetElement on receiver, not its prototype, or getters won't | |
544 // have the correct receiver. | |
545 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | |
546 isolate, element_value, | |
547 Object::GetElement(isolate, receiver, j), false); | |
548 visitor->visit(j, element_value); | |
549 } | |
550 } | |
551 } | |
552 break; | |
553 } | |
554 case FAST_HOLEY_DOUBLE_ELEMENTS: | |
555 case FAST_DOUBLE_ELEMENTS: { | |
556 // Empty array is FixedArray but not FixedDoubleArray. | |
557 if (length == 0) break; | |
558 // Run through the elements FixedArray and use HasElement and GetElement | |
559 // to check the prototype for missing elements. | |
560 if (receiver->elements()->IsFixedArray()) { | |
561 DCHECK(receiver->elements()->length() == 0); | |
562 break; | |
563 } | |
564 Handle<FixedDoubleArray> elements( | |
565 FixedDoubleArray::cast(receiver->elements())); | |
566 int fast_length = static_cast<int>(length); | |
567 DCHECK(fast_length <= elements->length()); | |
568 for (int j = 0; j < fast_length; j++) { | |
569 HandleScope loop_scope(isolate); | |
570 if (!elements->is_the_hole(j)) { | |
571 double double_value = elements->get_scalar(j); | |
572 Handle<Object> element_value = | |
573 isolate->factory()->NewNumber(double_value); | |
574 visitor->visit(j, element_value); | |
575 } else { | |
576 Maybe<bool> maybe = JSReceiver::HasElement(receiver, j); | |
577 if (!maybe.IsJust()) return false; | |
578 if (maybe.FromJust()) { | |
579 // Call GetElement on receiver, not its prototype, or getters won't | |
580 // have the correct receiver. | |
581 Handle<Object> element_value; | |
582 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | |
583 isolate, element_value, | |
584 Object::GetElement(isolate, receiver, j), false); | |
585 visitor->visit(j, element_value); | |
586 } | |
587 } | |
588 } | |
589 break; | |
590 } | |
591 case DICTIONARY_ELEMENTS: { | |
592 Handle<SeededNumberDictionary> dict(receiver->element_dictionary()); | |
593 List<uint32_t> indices(dict->Capacity() / 2); | |
594 // Collect all indices in the object and the prototypes less | |
595 // than length. This might introduce duplicates in the indices list. | |
596 CollectElementIndices(receiver, length, &indices); | |
597 indices.Sort(&compareUInt32); | |
598 int j = 0; | |
599 int n = indices.length(); | |
600 while (j < n) { | |
601 HandleScope loop_scope(isolate); | |
602 uint32_t index = indices[j]; | |
603 Handle<Object> element; | |
604 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | |
605 isolate, element, Object::GetElement(isolate, receiver, index), | |
606 false); | |
607 visitor->visit(index, element); | |
608 // Skip to next different index (i.e., omit duplicates). | |
609 do { | |
610 j++; | |
611 } while (j < n && indices[j] == index); | |
612 } | |
613 break; | |
614 } | |
615 case UINT8_CLAMPED_ELEMENTS: { | |
616 Handle<FixedUint8ClampedArray> pixels( | |
617 FixedUint8ClampedArray::cast(receiver->elements())); | |
618 for (uint32_t j = 0; j < length; j++) { | |
619 Handle<Smi> e(Smi::FromInt(pixels->get_scalar(j)), isolate); | |
620 visitor->visit(j, e); | |
621 } | |
622 break; | |
623 } | |
624 case INT8_ELEMENTS: { | |
625 IterateTypedArrayElements<FixedInt8Array, int8_t>( | |
626 isolate, receiver, true, true, visitor); | |
627 break; | |
628 } | |
629 case UINT8_ELEMENTS: { | |
630 IterateTypedArrayElements<FixedUint8Array, uint8_t>( | |
631 isolate, receiver, true, true, visitor); | |
632 break; | |
633 } | |
634 case INT16_ELEMENTS: { | |
635 IterateTypedArrayElements<FixedInt16Array, int16_t>( | |
636 isolate, receiver, true, true, visitor); | |
637 break; | |
638 } | |
639 case UINT16_ELEMENTS: { | |
640 IterateTypedArrayElements<FixedUint16Array, uint16_t>( | |
641 isolate, receiver, true, true, visitor); | |
642 break; | |
643 } | |
644 case INT32_ELEMENTS: { | |
645 IterateTypedArrayElements<FixedInt32Array, int32_t>( | |
646 isolate, receiver, true, false, visitor); | |
647 break; | |
648 } | |
649 case UINT32_ELEMENTS: { | |
650 IterateTypedArrayElements<FixedUint32Array, uint32_t>( | |
651 isolate, receiver, true, false, visitor); | |
652 break; | |
653 } | |
654 case FLOAT32_ELEMENTS: { | |
655 IterateTypedArrayElements<FixedFloat32Array, float>( | |
656 isolate, receiver, false, false, visitor); | |
657 break; | |
658 } | |
659 case FLOAT64_ELEMENTS: { | |
660 IterateTypedArrayElements<FixedFloat64Array, double>( | |
661 isolate, receiver, false, false, visitor); | |
662 break; | |
663 } | |
664 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: | |
665 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: { | |
666 for (uint32_t index = 0; index < length; index++) { | |
667 HandleScope loop_scope(isolate); | |
668 Handle<Object> element; | |
669 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | |
670 isolate, element, Object::GetElement(isolate, receiver, index), | |
671 false); | |
672 visitor->visit(index, element); | |
673 } | |
674 break; | |
675 } | |
676 } | |
677 visitor->increase_index_offset(length); | |
678 return true; | |
679 } | |
680 | |
681 | |
682 static bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) { | |
683 HandleScope handle_scope(isolate); | |
684 if (!obj->IsSpecObject()) return false; | |
685 if (FLAG_harmony_concat_spreadable) { | |
686 Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol()); | |
687 Handle<Object> value; | |
688 MaybeHandle<Object> maybeValue = | |
689 i::Runtime::GetObjectProperty(isolate, obj, key); | |
690 if (maybeValue.ToHandle(&value)) { | |
691 if (!value->IsUndefined()) { | |
692 return value->BooleanValue(); | |
693 } | |
694 } | |
695 } | |
696 return obj->IsJSArray(); | |
697 } | |
698 | |
699 | |
700 /** | |
701 * Array::concat implementation. | |
702 * See ECMAScript 262, 15.4.4.4. | |
703 * TODO(581): Fix non-compliance for very large concatenations and update to | |
704 * following the ECMAScript 5 specification. | |
705 */ | |
706 RUNTIME_FUNCTION(Runtime_ArrayConcat) { | |
707 HandleScope handle_scope(isolate); | |
708 DCHECK(args.length() == 1); | |
709 | |
710 CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0); | |
711 int argument_count = static_cast<int>(arguments->length()->Number()); | |
712 RUNTIME_ASSERT(arguments->HasFastObjectElements()); | |
713 Handle<FixedArray> elements(FixedArray::cast(arguments->elements())); | |
714 | |
715 // Pass 1: estimate the length and number of elements of the result. | |
716 // The actual length can be larger if any of the arguments have getters | |
717 // that mutate other arguments (but will otherwise be precise). | |
718 // The number of elements is precise if there are no inherited elements. | |
719 | |
720 ElementsKind kind = FAST_SMI_ELEMENTS; | |
721 | |
722 uint32_t estimate_result_length = 0; | |
723 uint32_t estimate_nof_elements = 0; | |
724 for (int i = 0; i < argument_count; i++) { | |
725 HandleScope loop_scope(isolate); | |
726 Handle<Object> obj(elements->get(i), isolate); | |
727 uint32_t length_estimate; | |
728 uint32_t element_estimate; | |
729 if (obj->IsJSArray()) { | |
730 Handle<JSArray> array(Handle<JSArray>::cast(obj)); | |
731 length_estimate = static_cast<uint32_t>(array->length()->Number()); | |
732 if (length_estimate != 0) { | |
733 ElementsKind array_kind = | |
734 GetPackedElementsKind(array->map()->elements_kind()); | |
735 if (IsMoreGeneralElementsKindTransition(kind, array_kind)) { | |
736 kind = array_kind; | |
737 } | |
738 } | |
739 element_estimate = EstimateElementCount(array); | |
740 } else { | |
741 if (obj->IsHeapObject()) { | |
742 if (obj->IsNumber()) { | |
743 if (IsMoreGeneralElementsKindTransition(kind, FAST_DOUBLE_ELEMENTS)) { | |
744 kind = FAST_DOUBLE_ELEMENTS; | |
745 } | |
746 } else if (IsMoreGeneralElementsKindTransition(kind, FAST_ELEMENTS)) { | |
747 kind = FAST_ELEMENTS; | |
748 } | |
749 } | |
750 length_estimate = 1; | |
751 element_estimate = 1; | |
752 } | |
753 // Avoid overflows by capping at kMaxElementCount. | |
754 if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) { | |
755 estimate_result_length = JSObject::kMaxElementCount; | |
756 } else { | |
757 estimate_result_length += length_estimate; | |
758 } | |
759 if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) { | |
760 estimate_nof_elements = JSObject::kMaxElementCount; | |
761 } else { | |
762 estimate_nof_elements += element_estimate; | |
763 } | |
764 } | |
765 | |
766 // If estimated number of elements is more than half of length, a | |
767 // fixed array (fast case) is more time and space-efficient than a | |
768 // dictionary. | |
769 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length; | |
770 | |
771 if (fast_case && kind == FAST_DOUBLE_ELEMENTS) { | |
772 Handle<FixedArrayBase> storage = | |
773 isolate->factory()->NewFixedDoubleArray(estimate_result_length); | |
774 int j = 0; | |
775 bool failure = false; | |
776 if (estimate_result_length > 0) { | |
777 Handle<FixedDoubleArray> double_storage = | |
778 Handle<FixedDoubleArray>::cast(storage); | |
779 for (int i = 0; i < argument_count; i++) { | |
780 Handle<Object> obj(elements->get(i), isolate); | |
781 if (obj->IsSmi()) { | |
782 double_storage->set(j, Smi::cast(*obj)->value()); | |
783 j++; | |
784 } else if (obj->IsNumber()) { | |
785 double_storage->set(j, obj->Number()); | |
786 j++; | |
787 } else { | |
788 JSArray* array = JSArray::cast(*obj); | |
789 uint32_t length = static_cast<uint32_t>(array->length()->Number()); | |
790 switch (array->map()->elements_kind()) { | |
791 case FAST_HOLEY_DOUBLE_ELEMENTS: | |
792 case FAST_DOUBLE_ELEMENTS: { | |
793 // Empty array is FixedArray but not FixedDoubleArray. | |
794 if (length == 0) break; | |
795 FixedDoubleArray* elements = | |
796 FixedDoubleArray::cast(array->elements()); | |
797 for (uint32_t i = 0; i < length; i++) { | |
798 if (elements->is_the_hole(i)) { | |
799 // TODO(jkummerow/verwaest): We could be a bit more clever | |
800 // here: Check if there are no elements/getters on the | |
801 // prototype chain, and if so, allow creation of a holey | |
802 // result array. | |
803 // Same thing below (holey smi case). | |
804 failure = true; | |
805 break; | |
806 } | |
807 double double_value = elements->get_scalar(i); | |
808 double_storage->set(j, double_value); | |
809 j++; | |
810 } | |
811 break; | |
812 } | |
813 case FAST_HOLEY_SMI_ELEMENTS: | |
814 case FAST_SMI_ELEMENTS: { | |
815 FixedArray* elements(FixedArray::cast(array->elements())); | |
816 for (uint32_t i = 0; i < length; i++) { | |
817 Object* element = elements->get(i); | |
818 if (element->IsTheHole()) { | |
819 failure = true; | |
820 break; | |
821 } | |
822 int32_t int_value = Smi::cast(element)->value(); | |
823 double_storage->set(j, int_value); | |
824 j++; | |
825 } | |
826 break; | |
827 } | |
828 case FAST_HOLEY_ELEMENTS: | |
829 case FAST_ELEMENTS: | |
830 case DICTIONARY_ELEMENTS: | |
831 DCHECK_EQ(0u, length); | |
832 break; | |
833 default: | |
834 UNREACHABLE(); | |
835 } | |
836 } | |
837 if (failure) break; | |
838 } | |
839 } | |
840 if (!failure) { | |
841 Handle<JSArray> array = isolate->factory()->NewJSArray(0); | |
842 Smi* length = Smi::FromInt(j); | |
843 Handle<Map> map; | |
844 map = JSObject::GetElementsTransitionMap(array, kind); | |
845 array->set_map(*map); | |
846 array->set_length(length); | |
847 array->set_elements(*storage); | |
848 return *array; | |
849 } | |
850 // In case of failure, fall through. | |
851 } | |
852 | |
853 Handle<FixedArray> storage; | |
854 if (fast_case) { | |
855 // The backing storage array must have non-existing elements to preserve | |
856 // holes across concat operations. | |
857 storage = | |
858 isolate->factory()->NewFixedArrayWithHoles(estimate_result_length); | |
859 } else { | |
860 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate | |
861 uint32_t at_least_space_for = | |
862 estimate_nof_elements + (estimate_nof_elements >> 2); | |
863 storage = Handle<FixedArray>::cast( | |
864 SeededNumberDictionary::New(isolate, at_least_space_for)); | |
865 } | |
866 | |
867 ArrayConcatVisitor visitor(isolate, storage, fast_case); | |
868 | |
869 for (int i = 0; i < argument_count; i++) { | |
870 Handle<Object> obj(elements->get(i), isolate); | |
871 bool spreadable = IsConcatSpreadable(isolate, obj); | |
872 if (isolate->has_pending_exception()) return isolate->heap()->exception(); | |
873 if (spreadable) { | |
874 Handle<JSObject> object = Handle<JSObject>::cast(obj); | |
875 if (!IterateElements(isolate, object, &visitor)) { | |
876 return isolate->heap()->exception(); | |
877 } | |
878 } else { | |
879 visitor.visit(0, obj); | |
880 visitor.increase_index_offset(1); | |
881 } | |
882 } | |
883 | |
884 if (visitor.exceeds_array_limit()) { | |
885 THROW_NEW_ERROR_RETURN_FAILURE( | |
886 isolate, NewRangeError(MessageTemplate::kInvalidArrayLength)); | |
887 } | |
888 return *visitor.ToArray(); | |
889 } | |
890 | |
891 | |
892 // Moves all own elements of an object, that are below a limit, to positions | 114 // Moves all own elements of an object, that are below a limit, to positions |
893 // starting at zero. All undefined values are placed after non-undefined values, | 115 // starting at zero. All undefined values are placed after non-undefined values, |
894 // and are followed by non-existing element. Does not change the length | 116 // and are followed by non-existing element. Does not change the length |
895 // property. | 117 // property. |
896 // Returns the number of non-undefined elements collected. | 118 // Returns the number of non-undefined elements collected. |
897 // Returns -1 if hole removal is not supported by this method. | 119 // Returns -1 if hole removal is not supported by this method. |
898 RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) { | 120 RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) { |
899 HandleScope scope(isolate); | 121 HandleScope scope(isolate); |
900 DCHECK(args.length() == 2); | 122 DCHECK(args.length() == 2); |
901 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); | 123 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); |
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1216 | 438 |
1217 uint32_t new_capacity = JSObject::NewElementsCapacity(index + 1); | 439 uint32_t new_capacity = JSObject::NewElementsCapacity(index + 1); |
1218 object->GetElementsAccessor()->GrowCapacityAndConvert(object, new_capacity); | 440 object->GetElementsAccessor()->GrowCapacityAndConvert(object, new_capacity); |
1219 } | 441 } |
1220 | 442 |
1221 // On success, return the fixed array elements. | 443 // On success, return the fixed array elements. |
1222 return object->elements(); | 444 return object->elements(); |
1223 } | 445 } |
1224 | 446 |
1225 | 447 |
448 RUNTIME_FUNCTION(Runtime_ArrayConcat) { | |
Camillo Bruni
2015/09/04 13:54:55
Again, I need to see on how to remove this.
| |
449 UNREACHABLE(); | |
450 return args[0]; | |
451 } | |
452 | |
453 | |
1226 RUNTIME_FUNCTION(Runtime_HasComplexElements) { | 454 RUNTIME_FUNCTION(Runtime_HasComplexElements) { |
1227 HandleScope scope(isolate); | 455 HandleScope scope(isolate); |
1228 DCHECK(args.length() == 1); | 456 DCHECK(args.length() == 1); |
1229 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0); | 457 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0); |
1230 for (PrototypeIterator iter(isolate, array, | 458 for (PrototypeIterator iter(isolate, array, |
1231 PrototypeIterator::START_AT_RECEIVER); | 459 PrototypeIterator::START_AT_RECEIVER); |
1232 !iter.IsAtEnd(); iter.Advance()) { | 460 !iter.IsAtEnd(); iter.Advance()) { |
1233 if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { | 461 if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { |
1234 return isolate->heap()->true_value(); | 462 return isolate->heap()->true_value(); |
1235 } | 463 } |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1272 | 500 |
1273 RUNTIME_FUNCTION(Runtime_FastOneByteArrayJoin) { | 501 RUNTIME_FUNCTION(Runtime_FastOneByteArrayJoin) { |
1274 SealHandleScope shs(isolate); | 502 SealHandleScope shs(isolate); |
1275 DCHECK(args.length() == 2); | 503 DCHECK(args.length() == 2); |
1276 // Returning undefined means that this fast path fails and one has to resort | 504 // Returning undefined means that this fast path fails and one has to resort |
1277 // to a slow path. | 505 // to a slow path. |
1278 return isolate->heap()->undefined_value(); | 506 return isolate->heap()->undefined_value(); |
1279 } | 507 } |
1280 } // namespace internal | 508 } // namespace internal |
1281 } // namespace v8 | 509 } // namespace v8 |
OLD | NEW |