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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |