Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(185)

Side by Side Diff: src/runtime/runtime-array.cc

Issue 1330483003: Adding ElementsAccessor::Concat (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@2015-09-01_array_builtins_shift
Patch Set: merging master Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/runtime/runtime.h ('k') | test/mjsunit/array-natives-elements.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
45 DCHECK(args.length() == 0); 45 DCHECK(args.length() == 0);
46 Handle<JSObject> holder = 46 Handle<JSObject> holder =
47 isolate->factory()->NewJSObject(isolate->object_function()); 47 isolate->factory()->NewJSObject(isolate->object_function());
48 48
49 InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop); 49 InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop);
50 InstallBuiltin(isolate, holder, "push", Builtins::kArrayPush); 50 InstallBuiltin(isolate, holder, "push", Builtins::kArrayPush);
51 InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift); 51 InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift);
52 InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift); 52 InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift);
53 InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice); 53 InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
54 InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice); 54 InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
55 InstallBuiltin(isolate, holder, "concat", Builtins::kArrayConcat);
56 55
57 return *holder; 56 return *holder;
58 } 57 }
59 58
60 59
61 RUNTIME_FUNCTION(Runtime_FixedArrayGet) { 60 RUNTIME_FUNCTION(Runtime_FixedArrayGet) {
62 SealHandleScope shs(isolate); 61 SealHandleScope shs(isolate);
63 DCHECK(args.length() == 2); 62 DCHECK(args.length() == 2);
64 CONVERT_ARG_CHECKED(FixedArray, object, 0); 63 CONVERT_ARG_CHECKED(FixedArray, object, 0);
65 CONVERT_SMI_ARG_CHECKED(index, 1); 64 CONVERT_SMI_ARG_CHECKED(index, 1);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 } 103 }
105 104
106 // Strict not needed. Used for cycle detection in Array join implementation. 105 // Strict not needed. Used for cycle detection in Array join implementation.
107 RETURN_FAILURE_ON_EXCEPTION( 106 RETURN_FAILURE_ON_EXCEPTION(
108 isolate, JSObject::AddDataElement(array, length, element, NONE)); 107 isolate, JSObject::AddDataElement(array, length, element, NONE));
109 JSObject::ValidateElements(array); 108 JSObject::ValidateElements(array);
110 return isolate->heap()->true_value(); 109 return isolate->heap()->true_value();
111 } 110 }
112 111
113 112
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 kind = GetMoreGeneralElementsKind(
734 kind, GetPackedElementsKind(array->map()->elements_kind()));
735 }
736 element_estimate = EstimateElementCount(array);
737 } else {
738 if (obj->IsHeapObject()) {
739 if (obj->IsNumber()) {
740 kind = GetMoreGeneralElementsKind(kind, FAST_DOUBLE_ELEMENTS);
741 } else {
742 kind = GetMoreGeneralElementsKind(kind, FAST_ELEMENTS);
743 }
744 }
745 length_estimate = 1;
746 element_estimate = 1;
747 }
748 // Avoid overflows by capping at kMaxElementCount.
749 if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) {
750 estimate_result_length = JSObject::kMaxElementCount;
751 } else {
752 estimate_result_length += length_estimate;
753 }
754 if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) {
755 estimate_nof_elements = JSObject::kMaxElementCount;
756 } else {
757 estimate_nof_elements += element_estimate;
758 }
759 }
760
761 // If estimated number of elements is more than half of length, a
762 // fixed array (fast case) is more time and space-efficient than a
763 // dictionary.
764 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length;
765
766 if (fast_case && kind == FAST_DOUBLE_ELEMENTS) {
767 Handle<FixedArrayBase> storage =
768 isolate->factory()->NewFixedDoubleArray(estimate_result_length);
769 int j = 0;
770 bool failure = false;
771 if (estimate_result_length > 0) {
772 Handle<FixedDoubleArray> double_storage =
773 Handle<FixedDoubleArray>::cast(storage);
774 for (int i = 0; i < argument_count; i++) {
775 Handle<Object> obj(elements->get(i), isolate);
776 if (obj->IsSmi()) {
777 double_storage->set(j, Smi::cast(*obj)->value());
778 j++;
779 } else if (obj->IsNumber()) {
780 double_storage->set(j, obj->Number());
781 j++;
782 } else {
783 JSArray* array = JSArray::cast(*obj);
784 uint32_t length = static_cast<uint32_t>(array->length()->Number());
785 switch (array->map()->elements_kind()) {
786 case FAST_HOLEY_DOUBLE_ELEMENTS:
787 case FAST_DOUBLE_ELEMENTS: {
788 // Empty array is FixedArray but not FixedDoubleArray.
789 if (length == 0) break;
790 FixedDoubleArray* elements =
791 FixedDoubleArray::cast(array->elements());
792 for (uint32_t i = 0; i < length; i++) {
793 if (elements->is_the_hole(i)) {
794 // TODO(jkummerow/verwaest): We could be a bit more clever
795 // here: Check if there are no elements/getters on the
796 // prototype chain, and if so, allow creation of a holey
797 // result array.
798 // Same thing below (holey smi case).
799 failure = true;
800 break;
801 }
802 double double_value = elements->get_scalar(i);
803 double_storage->set(j, double_value);
804 j++;
805 }
806 break;
807 }
808 case FAST_HOLEY_SMI_ELEMENTS:
809 case FAST_SMI_ELEMENTS: {
810 FixedArray* elements(FixedArray::cast(array->elements()));
811 for (uint32_t i = 0; i < length; i++) {
812 Object* element = elements->get(i);
813 if (element->IsTheHole()) {
814 failure = true;
815 break;
816 }
817 int32_t int_value = Smi::cast(element)->value();
818 double_storage->set(j, int_value);
819 j++;
820 }
821 break;
822 }
823 case FAST_HOLEY_ELEMENTS:
824 case FAST_ELEMENTS:
825 case DICTIONARY_ELEMENTS:
826 DCHECK_EQ(0u, length);
827 break;
828 default:
829 UNREACHABLE();
830 }
831 }
832 if (failure) break;
833 }
834 }
835 if (!failure) {
836 Handle<JSArray> array = isolate->factory()->NewJSArray(0);
837 Smi* length = Smi::FromInt(j);
838 Handle<Map> map;
839 map = JSObject::GetElementsTransitionMap(array, kind);
840 array->set_map(*map);
841 array->set_length(length);
842 array->set_elements(*storage);
843 return *array;
844 }
845 // In case of failure, fall through.
846 }
847
848 Handle<FixedArray> storage;
849 if (fast_case) {
850 // The backing storage array must have non-existing elements to preserve
851 // holes across concat operations.
852 storage =
853 isolate->factory()->NewFixedArrayWithHoles(estimate_result_length);
854 } else {
855 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
856 uint32_t at_least_space_for =
857 estimate_nof_elements + (estimate_nof_elements >> 2);
858 storage = Handle<FixedArray>::cast(
859 SeededNumberDictionary::New(isolate, at_least_space_for));
860 }
861
862 ArrayConcatVisitor visitor(isolate, storage, fast_case);
863
864 for (int i = 0; i < argument_count; i++) {
865 Handle<Object> obj(elements->get(i), isolate);
866 bool spreadable = IsConcatSpreadable(isolate, obj);
867 if (isolate->has_pending_exception()) return isolate->heap()->exception();
868 if (spreadable) {
869 Handle<JSObject> object = Handle<JSObject>::cast(obj);
870 if (!IterateElements(isolate, object, &visitor)) {
871 return isolate->heap()->exception();
872 }
873 } else {
874 visitor.visit(0, obj);
875 visitor.increase_index_offset(1);
876 }
877 }
878
879 if (visitor.exceeds_array_limit()) {
880 THROW_NEW_ERROR_RETURN_FAILURE(
881 isolate, NewRangeError(MessageTemplate::kInvalidArrayLength));
882 }
883 return *visitor.ToArray();
884 }
885
886
887 // Moves all own elements of an object, that are below a limit, to positions 113 // Moves all own elements of an object, that are below a limit, to positions
888 // starting at zero. All undefined values are placed after non-undefined values, 114 // starting at zero. All undefined values are placed after non-undefined values,
889 // and are followed by non-existing element. Does not change the length 115 // and are followed by non-existing element. Does not change the length
890 // property. 116 // property.
891 // Returns the number of non-undefined elements collected. 117 // Returns the number of non-undefined elements collected.
892 // Returns -1 if hole removal is not supported by this method. 118 // Returns -1 if hole removal is not supported by this method.
893 RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) { 119 RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) {
894 HandleScope scope(isolate); 120 HandleScope scope(isolate);
895 DCHECK(args.length() == 2); 121 DCHECK(args.length() == 2);
896 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); 122 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after
1267 493
1268 RUNTIME_FUNCTION(Runtime_FastOneByteArrayJoin) { 494 RUNTIME_FUNCTION(Runtime_FastOneByteArrayJoin) {
1269 SealHandleScope shs(isolate); 495 SealHandleScope shs(isolate);
1270 DCHECK(args.length() == 2); 496 DCHECK(args.length() == 2);
1271 // Returning undefined means that this fast path fails and one has to resort 497 // Returning undefined means that this fast path fails and one has to resort
1272 // to a slow path. 498 // to a slow path.
1273 return isolate->heap()->undefined_value(); 499 return isolate->heap()->undefined_value();
1274 } 500 }
1275 } // namespace internal 501 } // namespace internal
1276 } // namespace v8 502 } // namespace v8
OLDNEW
« no previous file with comments | « src/runtime/runtime.h ('k') | test/mjsunit/array-natives-elements.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698