• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "src/arguments-inl.h"
6 #include "src/code-stubs.h"
7 #include "src/conversions-inl.h"
8 #include "src/debug/debug.h"
9 #include "src/elements.h"
10 #include "src/heap/factory.h"
11 #include "src/isolate-inl.h"
12 #include "src/keys.h"
13 #include "src/messages.h"
14 #include "src/objects/arguments-inl.h"
15 #include "src/objects/hash-table-inl.h"
16 #include "src/objects/js-array-inl.h"
17 #include "src/prototype.h"
18 #include "src/runtime/runtime-utils.h"
19 
20 namespace v8 {
21 namespace internal {
22 
RUNTIME_FUNCTION(Runtime_TransitionElementsKind)23 RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
24   HandleScope scope(isolate);
25   DCHECK_EQ(2, args.length());
26   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
27   CONVERT_ARG_HANDLE_CHECKED(Map, to_map, 1);
28   ElementsKind to_kind = to_map->elements_kind();
29   ElementsAccessor::ForKind(to_kind)->TransitionElementsKind(object, to_map);
30   return *object;
31 }
32 
33 namespace {
34 // Find the next free position. undefined and holes are both considered
35 // free spots. Returns "Nothing" if an exception occurred.
36 V8_WARN_UNUSED_RESULT
FindNextFreePosition(Isolate * isolate,Handle<JSReceiver> receiver,uint32_t current_pos)37 Maybe<uint32_t> FindNextFreePosition(Isolate* isolate,
38                                      Handle<JSReceiver> receiver,
39                                      uint32_t current_pos) {
40   for (uint32_t position = current_pos;; ++position) {
41     Maybe<bool> has_element = JSReceiver::HasElement(receiver, position);
42     MAYBE_RETURN(has_element, Nothing<uint32_t>());
43     if (!has_element.FromJust()) return Just(position);
44 
45     Handle<Object> element;
46     ASSIGN_RETURN_ON_EXCEPTION_VALUE(
47         isolate, element, JSReceiver::GetElement(isolate, receiver, position),
48         Nothing<uint32_t>());
49     if (element->IsUndefined(isolate)) return Just(position);
50   }
51 }
52 
53 // As RemoveArrayHoles, but also handles Dictionary elements that stay
54 // Dictionary (requires_slow_elements() is true), proxies and objects that
55 // might have accessors.
56 V8_WARN_UNUSED_RESULT
RemoveArrayHolesGeneric(Isolate * isolate,Handle<JSReceiver> receiver,uint32_t limit)57 Object* RemoveArrayHolesGeneric(Isolate* isolate, Handle<JSReceiver> receiver,
58                                 uint32_t limit) {
59   HandleScope scope(isolate);
60 
61   // For proxies, we do not collect the keys, instead we use all indices in
62   // the full range of [0, limit).
63   Handle<FixedArray> keys;
64   if (!receiver->IsJSProxy()) {
65     keys = JSReceiver::GetOwnElementIndices(isolate, receiver,
66                                             Handle<JSObject>::cast(receiver));
67   }
68 
69   uint32_t num_undefined = 0;
70   uint32_t current_pos = 0;
71   int num_indices = keys.is_null() ? limit : keys->length();
72 
73   // Compact keys with undefined values and moves non-undefined
74   // values to the front.
75   // The loop does two things simultaneously:
76   //   (1) Count the number of 'undefined', i.e.
77   //       i.e.: HasProperty(receiver, key) && Get(receiver, key) == undefined
78   //   (2) Move all non-undefined values to the front. The variable current_pos
79   //       is used to track free spots in the array starting at the beginning.
80   //       Holes and 'undefined' are considered free spots.
81   //       A hole is when HasElement(receiver, key) is false.
82   for (int i = 0; i < num_indices; ++i) {
83     uint32_t key = keys.is_null() ? i : NumberToUint32(keys->get(i));
84 
85     // We only care about array indices that are smaller than the limit.
86     // The keys are sorted, so we can break as soon as we encounter the first.
87     if (key >= limit) break;
88 
89     Maybe<bool> has_element = JSReceiver::HasElement(receiver, key);
90     MAYBE_RETURN(has_element, ReadOnlyRoots(isolate).exception());
91     if (!has_element.FromJust()) {
92       continue;
93     }
94 
95     Handle<Object> element;
96     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
97         isolate, element, JSReceiver::GetElement(isolate, receiver, key));
98 
99     if (element->IsUndefined(isolate)) {
100       ++num_undefined;
101     } else {
102       // Find next free position to move elements to.
103       Maybe<uint32_t> free_position =
104           FindNextFreePosition(isolate, receiver, current_pos);
105       MAYBE_RETURN(free_position, ReadOnlyRoots(isolate).exception());
106       current_pos = free_position.FromJust();
107 
108       // Do not move elements that are already in the "packed" area.
109       if (key <= current_pos) continue;
110 
111       // array[current_pos] = array[key].
112       // Deleting array[key] is done later. This is to preserve the same
113       // semantics as the old JS implementation when working with non-extensible
114       // objects:
115       // If the array contains undefineds, the position at 'key' might later
116       // bet set to 'undefined'. If we delete the element now and later set it
117       // to undefined, the set operation would throw an exception.
118       RETURN_FAILURE_ON_EXCEPTION(
119           isolate, JSReceiver::SetElement(isolate, receiver, current_pos,
120                                           element, LanguageMode::kStrict));
121       ++current_pos;
122     }
123   }
124 
125   // Set [current_pos, current_pos + num_undefined) to undefined.
126   uint32_t result = current_pos;
127   for (uint32_t i = 0; i < num_undefined; ++i) {
128     RETURN_FAILURE_ON_EXCEPTION(
129         isolate, JSReceiver::SetElement(isolate, receiver, current_pos++,
130                                         isolate->factory()->undefined_value(),
131                                         LanguageMode::kStrict));
132   }
133   // TODO(szuend): Re-enable when we also copy from the prototype chain for
134   //               JSArrays. Then we can use HasOwnProperty instead of
135   //               HasElement and this condition will hold.
136   // DCHECK_LE(current_pos, num_indices);
137 
138   // Deleting everything after the undefineds up unto the limit.
139   for (int i = num_indices - 1; i >= 0; --i) {
140     uint32_t key = keys.is_null() ? i : NumberToUint32(keys->get(i));
141     if (key < current_pos) break;
142     if (key >= limit) continue;
143 
144     Maybe<bool> delete_result = JSReceiver::DeleteElement(receiver, key);
145     MAYBE_RETURN(delete_result, ReadOnlyRoots(isolate).exception());
146   }
147 
148   // TODO(jgruber, szuend, chromium:897512): This is a workaround to prevent
149   // returning a number greater than array.length to Array.p.sort, which could
150   // trigger OOB accesses. There is still a correctness bug here though in
151   // how we shift around undefineds and delete elements in the two blocks above.
152   // This needs to be fixed soon.
153   const uint32_t number_of_non_undefined_elements = std::min(limit, result);
154 
155   return *isolate->factory()->NewNumberFromUint(
156       number_of_non_undefined_elements);
157 }
158 
159 // Collects all defined (non-hole) and non-undefined (array) elements at the
160 // start of the elements array.  If the object is in dictionary mode, it is
161 // converted to fast elements mode.  Undefined values are placed after
162 // non-undefined values.  Returns the number of non-undefined values.
163 V8_WARN_UNUSED_RESULT
RemoveArrayHoles(Isolate * isolate,Handle<JSReceiver> receiver,uint32_t limit)164 Object* RemoveArrayHoles(Isolate* isolate, Handle<JSReceiver> receiver,
165                          uint32_t limit) {
166   if (receiver->IsJSProxy()) {
167     return RemoveArrayHolesGeneric(isolate, receiver, limit);
168   }
169 
170   Handle<JSObject> object = Handle<JSObject>::cast(receiver);
171   if (object->HasStringWrapperElements()) {
172     int len = String::cast(Handle<JSValue>::cast(object)->value())->length();
173     DCHECK_LE(len, limit);
174     return Smi::FromInt(len);
175   }
176 
177   if (object->HasSloppyArgumentsElements() || !object->map()->is_extensible()) {
178     return RemoveArrayHolesGeneric(isolate, receiver, limit);
179   }
180 
181   JSObject::ValidateElements(*object);
182   if (object->HasDictionaryElements()) {
183     // Convert to fast elements containing only the existing properties.
184     // Ordering is irrelevant, since we are going to sort anyway.
185     Handle<NumberDictionary> dict(object->element_dictionary(), isolate);
186     if (object->IsJSArray() || dict->requires_slow_elements() ||
187         dict->max_number_key() >= limit) {
188       return RemoveArrayHolesGeneric(isolate, receiver, limit);
189     }
190     // Convert to fast elements.
191     Handle<Map> new_map =
192         JSObject::GetElementsTransitionMap(object, HOLEY_ELEMENTS);
193 
194     PretenureFlag tenure = Heap::InNewSpace(*object) ? NOT_TENURED : TENURED;
195     Handle<FixedArray> fast_elements =
196         isolate->factory()->NewFixedArray(dict->NumberOfElements(), tenure);
197     dict->CopyValuesTo(*fast_elements);
198 
199     JSObject::SetMapAndElements(object, new_map, fast_elements);
200     JSObject::ValidateElements(*object);
201   } else if (object->HasFixedTypedArrayElements()) {
202     // Typed arrays cannot have holes or undefined elements.
203     int array_length = FixedArrayBase::cast(object->elements())->length();
204     return Smi::FromInt(Min(limit, static_cast<uint32_t>(array_length)));
205   } else if (!object->HasDoubleElements()) {
206     JSObject::EnsureWritableFastElements(object);
207   }
208   DCHECK(object->HasSmiOrObjectElements() || object->HasDoubleElements());
209 
210   // Collect holes at the end, undefined before that and the rest at the
211   // start, and return the number of non-hole, non-undefined values.
212 
213   Handle<FixedArrayBase> elements_base(object->elements(), isolate);
214   uint32_t elements_length = static_cast<uint32_t>(elements_base->length());
215   if (limit > elements_length) {
216     limit = elements_length;
217   }
218   if (limit == 0) {
219     return Smi::kZero;
220   }
221 
222   uint32_t result = 0;
223   if (elements_base->map() == ReadOnlyRoots(isolate).fixed_double_array_map()) {
224     FixedDoubleArray* elements = FixedDoubleArray::cast(*elements_base);
225     // Split elements into defined and the_hole, in that order.
226     unsigned int holes = limit;
227     // Assume most arrays contain no holes and undefined values, so minimize the
228     // number of stores of non-undefined, non-the-hole values.
229     for (unsigned int i = 0; i < holes; i++) {
230       if (elements->is_the_hole(i)) {
231         holes--;
232       } else {
233         continue;
234       }
235       // Position i needs to be filled.
236       while (holes > i) {
237         if (elements->is_the_hole(holes)) {
238           holes--;
239         } else {
240           elements->set(i, elements->get_scalar(holes));
241           break;
242         }
243       }
244     }
245     result = holes;
246     while (holes < limit) {
247       elements->set_the_hole(holes);
248       holes++;
249     }
250   } else {
251     FixedArray* elements = FixedArray::cast(*elements_base);
252     DisallowHeapAllocation no_gc;
253 
254     // Split elements into defined, undefined and the_hole, in that order.  Only
255     // count locations for undefined and the hole, and fill them afterwards.
256     WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_gc);
257     unsigned int undefs = limit;
258     unsigned int holes = limit;
259     // Assume most arrays contain no holes and undefined values, so minimize the
260     // number of stores of non-undefined, non-the-hole values.
261     for (unsigned int i = 0; i < undefs; i++) {
262       Object* current = elements->get(i);
263       if (current->IsTheHole(isolate)) {
264         holes--;
265         undefs--;
266       } else if (current->IsUndefined(isolate)) {
267         undefs--;
268       } else {
269         continue;
270       }
271       // Position i needs to be filled.
272       while (undefs > i) {
273         current = elements->get(undefs);
274         if (current->IsTheHole(isolate)) {
275           holes--;
276           undefs--;
277         } else if (current->IsUndefined(isolate)) {
278           undefs--;
279         } else {
280           elements->set(i, current, write_barrier);
281           break;
282         }
283       }
284     }
285     result = undefs;
286     while (undefs < holes) {
287       elements->set_undefined(isolate, undefs);
288       undefs++;
289     }
290     while (holes < limit) {
291       elements->set_the_hole(isolate, holes);
292       holes++;
293     }
294   }
295 
296   DCHECK_LE(result, limit);
297   return *isolate->factory()->NewNumberFromUint(result);
298 }
299 
300 // Copy element at index from source to target only if target does not have the
301 // element on its own. Returns true if a copy occurred, false if not
302 // and Nothing if an exception occurred.
303 V8_WARN_UNUSED_RESULT
ConditionalCopy(Isolate * isolate,Handle<JSReceiver> source,Handle<JSReceiver> target,uint32_t index)304 Maybe<bool> ConditionalCopy(Isolate* isolate, Handle<JSReceiver> source,
305                             Handle<JSReceiver> target, uint32_t index) {
306   Maybe<bool> source_has_prop = JSReceiver::HasOwnProperty(source, index);
307   MAYBE_RETURN(source_has_prop, Nothing<bool>());
308   if (!source_has_prop.FromJust()) return Just(false);
309 
310   Maybe<bool> target_has_prop = JSReceiver::HasOwnProperty(target, index);
311   MAYBE_RETURN(target_has_prop, Nothing<bool>());
312   if (target_has_prop.FromJust()) return Just(false);
313 
314   Handle<Object> source_element;
315   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
316       isolate, source_element, JSReceiver::GetElement(isolate, source, index),
317       Nothing<bool>());
318 
319   Handle<Object> set_result;
320   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
321       isolate, set_result,
322       JSReceiver::SetElement(isolate, target, index, source_element,
323                              LanguageMode::kStrict),
324       Nothing<bool>());
325 
326   return Just(true);
327 }
328 
329 // Copy elements in the range 0..length from objects prototype chain
330 // to object itself, if object has holes. Returns null on error and undefined on
331 // success.
332 V8_WARN_UNUSED_RESULT
CopyFromPrototype(Isolate * isolate,Handle<JSReceiver> object,uint32_t length)333 MaybeHandle<Object> CopyFromPrototype(Isolate* isolate,
334                                       Handle<JSReceiver> object,
335                                       uint32_t length) {
336   for (PrototypeIterator iter(isolate, object, kStartAtPrototype);
337        !iter.IsAtEnd(); iter.Advance()) {
338     Handle<JSReceiver> current(PrototypeIterator::GetCurrent<JSReceiver>(iter));
339 
340     if (current->IsJSProxy()) {
341       for (uint32_t i = 0; i < length; ++i) {
342         MAYBE_RETURN_NULL(ConditionalCopy(isolate, current, object, i));
343       }
344     } else {
345       Handle<FixedArray> keys = JSReceiver::GetOwnElementIndices(
346           isolate, object, Handle<JSObject>::cast(current));
347 
348       uint32_t num_indices = keys->length();
349       for (uint32_t i = 0; i < num_indices; ++i) {
350         uint32_t idx = NumberToUint32(keys->get(i));
351 
352         // Prototype might have indices that go past length, but we are only
353         // interested in the range [0, length).
354         if (idx >= length) break;
355 
356         MAYBE_RETURN_NULL(ConditionalCopy(isolate, current, object, idx));
357       }
358     }
359   }
360   return isolate->factory()->undefined_value();
361 }
362 
363 }  // namespace
364 
RUNTIME_FUNCTION(Runtime_PrepareElementsForSort)365 RUNTIME_FUNCTION(Runtime_PrepareElementsForSort) {
366   HandleScope scope(isolate);
367   DCHECK_EQ(2, args.length());
368   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0);
369   CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
370 
371   if (isolate->debug_execution_mode() == DebugInfo::kSideEffects) {
372     if (!isolate->debug()->PerformSideEffectCheckForObject(object)) {
373       return ReadOnlyRoots(isolate).exception();
374     }
375   }
376 
377   // Counter for sorting arrays that have non-packed elements and where either
378   // the ElementsProtector is invalid or the prototype does not match
379   // Array.prototype.
380   if (object->IsJSArray() &&
381       !Handle<JSArray>::cast(object)->HasFastPackedElements()) {
382     JSObject* initial_array_proto = JSObject::cast(
383         isolate->native_context()->get(Context::INITIAL_ARRAY_PROTOTYPE_INDEX));
384     if (!isolate->IsNoElementsProtectorIntact() ||
385         object->map()->prototype() != initial_array_proto) {
386       isolate->CountUsage(
387           v8::Isolate::kArrayPrototypeSortJSArrayModifiedPrototype);
388     }
389   }
390 
391   if (!object->IsJSArray()) {
392     RETURN_FAILURE_ON_EXCEPTION(isolate,
393                                 CopyFromPrototype(isolate, object, length));
394   }
395   return RemoveArrayHoles(isolate, object, length);
396 }
397 
398 // Move contents of argument 0 (an array) to argument 1 (an array)
RUNTIME_FUNCTION(Runtime_MoveArrayContents)399 RUNTIME_FUNCTION(Runtime_MoveArrayContents) {
400   HandleScope scope(isolate);
401   DCHECK_EQ(2, args.length());
402   CONVERT_ARG_HANDLE_CHECKED(JSArray, from, 0);
403   CONVERT_ARG_HANDLE_CHECKED(JSArray, to, 1);
404   JSObject::ValidateElements(*from);
405   JSObject::ValidateElements(*to);
406 
407   Handle<FixedArrayBase> new_elements(from->elements(), isolate);
408   ElementsKind from_kind = from->GetElementsKind();
409   Handle<Map> new_map = JSObject::GetElementsTransitionMap(to, from_kind);
410   JSObject::SetMapAndElements(to, new_map, new_elements);
411   to->set_length(from->length());
412 
413   from->initialize_elements();
414   from->set_length(Smi::kZero);
415 
416   JSObject::ValidateElements(*to);
417   return *to;
418 }
419 
420 
421 // How many elements does this object/array have?
RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements)422 RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements) {
423   DisallowHeapAllocation no_gc;
424   HandleScope scope(isolate);
425   DCHECK_EQ(1, args.length());
426   CONVERT_ARG_CHECKED(JSArray, array, 0);
427   FixedArrayBase* elements = array->elements();
428   SealHandleScope shs(isolate);
429   if (elements->IsNumberDictionary()) {
430     int result = NumberDictionary::cast(elements)->NumberOfElements();
431     return Smi::FromInt(result);
432   } else {
433     DCHECK(array->length()->IsSmi());
434     // For packed elements, we know the exact number of elements
435     int length = elements->length();
436     ElementsKind kind = array->GetElementsKind();
437     if (IsFastPackedElementsKind(kind)) {
438       return Smi::FromInt(length);
439     }
440     // For holey elements, take samples from the buffer checking for holes
441     // to generate the estimate.
442     const int kNumberOfHoleCheckSamples = 97;
443     int increment = (length < kNumberOfHoleCheckSamples)
444                         ? 1
445                         : static_cast<int>(length / kNumberOfHoleCheckSamples);
446     ElementsAccessor* accessor = array->GetElementsAccessor();
447     int holes = 0;
448     for (int i = 0; i < length; i += increment) {
449       if (!accessor->HasElement(array, i, elements)) {
450         ++holes;
451       }
452     }
453     int estimate = static_cast<int>((kNumberOfHoleCheckSamples - holes) /
454                                     kNumberOfHoleCheckSamples * length);
455     return Smi::FromInt(estimate);
456   }
457 }
458 
459 
460 // Returns an array that tells you where in the [0, length) interval an array
461 // might have elements.  Can either return an array of keys (positive integers
462 // or undefined) or a number representing the positive length of an interval
463 // starting at index 0.
464 // Intervals can span over some keys that are not in the object.
RUNTIME_FUNCTION(Runtime_GetArrayKeys)465 RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
466   HandleScope scope(isolate);
467   DCHECK_EQ(2, args.length());
468   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
469   CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
470   ElementsKind kind = array->GetElementsKind();
471 
472   if (IsFastElementsKind(kind) || IsFixedTypedArrayElementsKind(kind)) {
473     uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
474     return *isolate->factory()->NewNumberFromUint(Min(actual_length, length));
475   }
476 
477   if (kind == FAST_STRING_WRAPPER_ELEMENTS) {
478     int string_length =
479         String::cast(Handle<JSValue>::cast(array)->value())->length();
480     int backing_store_length = array->elements()->length();
481     return *isolate->factory()->NewNumberFromUint(
482         Min(length,
483             static_cast<uint32_t>(Max(string_length, backing_store_length))));
484   }
485 
486   KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
487                              ALL_PROPERTIES);
488   for (PrototypeIterator iter(isolate, array, kStartAtReceiver);
489        !iter.IsAtEnd(); iter.Advance()) {
490     Handle<JSReceiver> current(PrototypeIterator::GetCurrent<JSReceiver>(iter));
491     if (current->HasComplexElements()) {
492       return *isolate->factory()->NewNumberFromUint(length);
493     }
494     accumulator.CollectOwnElementIndices(array,
495                                          Handle<JSObject>::cast(current));
496   }
497   // Erase any keys >= length.
498   Handle<FixedArray> keys =
499       accumulator.GetKeys(GetKeysConversion::kKeepNumbers);
500   int j = 0;
501   for (int i = 0; i < keys->length(); i++) {
502     if (NumberToUint32(keys->get(i)) >= length) continue;
503     if (i != j) keys->set(j, keys->get(i));
504     j++;
505   }
506 
507   keys = FixedArray::ShrinkOrEmpty(isolate, keys, j);
508   return *isolate->factory()->NewJSArrayWithElements(keys);
509 }
510 
RUNTIME_FUNCTION(Runtime_TrySliceSimpleNonFastElements)511 RUNTIME_FUNCTION(Runtime_TrySliceSimpleNonFastElements) {
512   HandleScope scope(isolate);
513   DCHECK_EQ(3, args.length());
514   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
515   CONVERT_SMI_ARG_CHECKED(first, 1);
516   CONVERT_SMI_ARG_CHECKED(count, 2);
517   uint32_t length = first + count;
518 
519   // Only handle elements kinds that have a ElementsAccessor Slice
520   // implementation.
521   if (receiver->IsJSArray()) {
522     // This "fastish" path must make sure the destination array is a JSArray.
523     if (!isolate->IsArraySpeciesLookupChainIntact() ||
524         !JSArray::cast(*receiver)->HasArrayPrototype(isolate)) {
525       return Smi::FromInt(0);
526     }
527   } else {
528     int len;
529     if (!receiver->IsJSObject() ||
530         !JSSloppyArgumentsObject::GetSloppyArgumentsLength(
531             isolate, Handle<JSObject>::cast(receiver), &len) ||
532         (length > static_cast<uint32_t>(len))) {
533       return Smi::FromInt(0);
534     }
535   }
536 
537   // This "fastish" path must also ensure that elements are simple (no
538   // geters/setters), no elements on prototype chain.
539   Handle<JSObject> object(Handle<JSObject>::cast(receiver));
540   if (!JSObject::PrototypeHasNoElements(isolate, *object) ||
541       object->HasComplexElements()) {
542     return Smi::FromInt(0);
543   }
544 
545   ElementsAccessor* accessor = object->GetElementsAccessor();
546   return *accessor->Slice(object, first, length);
547 }
548 
RUNTIME_FUNCTION(Runtime_NewArray)549 RUNTIME_FUNCTION(Runtime_NewArray) {
550   HandleScope scope(isolate);
551   DCHECK_LE(3, args.length());
552   int const argc = args.length() - 3;
553   // TODO(bmeurer): Remove this Arguments nonsense.
554   Arguments argv(argc, args.arguments() - 1);
555   CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0);
556   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, argc + 1);
557   CONVERT_ARG_HANDLE_CHECKED(HeapObject, type_info, argc + 2);
558   // TODO(bmeurer): Use MaybeHandle to pass around the AllocationSite.
559   Handle<AllocationSite> site = type_info->IsAllocationSite()
560                                     ? Handle<AllocationSite>::cast(type_info)
561                                     : Handle<AllocationSite>::null();
562 
563   Factory* factory = isolate->factory();
564 
565   // If called through new, new.target can be:
566   // - a subclass of constructor,
567   // - a proxy wrapper around constructor, or
568   // - the constructor itself.
569   // If called through Reflect.construct, it's guaranteed to be a constructor by
570   // REFLECT_CONSTRUCT_PREPARE.
571   DCHECK(new_target->IsConstructor());
572 
573   bool holey = false;
574   bool can_use_type_feedback = !site.is_null();
575   bool can_inline_array_constructor = true;
576   if (argv.length() == 1) {
577     Handle<Object> argument_one = argv.at<Object>(0);
578     if (argument_one->IsSmi()) {
579       int value = Handle<Smi>::cast(argument_one)->value();
580       if (value < 0 ||
581           JSArray::SetLengthWouldNormalize(isolate->heap(), value)) {
582         // the array is a dictionary in this case.
583         can_use_type_feedback = false;
584       } else if (value != 0) {
585         holey = true;
586         if (value >= JSArray::kInitialMaxFastElementArray) {
587           can_inline_array_constructor = false;
588         }
589       }
590     } else {
591       // Non-smi length argument produces a dictionary
592       can_use_type_feedback = false;
593     }
594   }
595 
596   Handle<Map> initial_map;
597   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
598       isolate, initial_map,
599       JSFunction::GetDerivedMap(isolate, constructor, new_target));
600 
601   ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind()
602                                                : initial_map->elements_kind();
603   if (holey && !IsHoleyElementsKind(to_kind)) {
604     to_kind = GetHoleyElementsKind(to_kind);
605     // Update the allocation site info to reflect the advice alteration.
606     if (!site.is_null()) site->SetElementsKind(to_kind);
607   }
608 
609   // We should allocate with an initial map that reflects the allocation site
610   // advice. Therefore we use AllocateJSObjectFromMap instead of passing
611   // the constructor.
612   initial_map = Map::AsElementsKind(isolate, initial_map, to_kind);
613 
614   // If we don't care to track arrays of to_kind ElementsKind, then
615   // don't emit a memento for them.
616   Handle<AllocationSite> allocation_site;
617   if (AllocationSite::ShouldTrack(to_kind)) {
618     allocation_site = site;
619   }
620 
621   Handle<JSArray> array = Handle<JSArray>::cast(
622       factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site));
623 
624   factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
625 
626   ElementsKind old_kind = array->GetElementsKind();
627   RETURN_FAILURE_ON_EXCEPTION(isolate,
628                               ArrayConstructInitializeElements(array, &argv));
629   if (!site.is_null()) {
630     if ((old_kind != array->GetElementsKind() || !can_use_type_feedback ||
631          !can_inline_array_constructor)) {
632       // The arguments passed in caused a transition. This kind of complexity
633       // can't be dealt with in the inlined optimized array constructor case.
634       // We must mark the allocationsite as un-inlinable.
635       site->SetDoNotInlineCall();
636     }
637   } else {
638     if (old_kind != array->GetElementsKind() || !can_inline_array_constructor) {
639       // We don't have an AllocationSite for this Array constructor invocation,
640       // i.e. it might a call from Array#map or from an Array subclass, so we
641       // just flip the bit on the global protector cell instead.
642       // TODO(bmeurer): Find a better way to mark this. Global protectors
643       // tend to back-fire over time...
644       if (isolate->IsArrayConstructorIntact()) {
645         isolate->InvalidateArrayConstructorProtector();
646       }
647     }
648   }
649 
650   return *array;
651 }
652 
RUNTIME_FUNCTION(Runtime_NormalizeElements)653 RUNTIME_FUNCTION(Runtime_NormalizeElements) {
654   HandleScope scope(isolate);
655   DCHECK_EQ(1, args.length());
656   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
657   CHECK(!array->HasFixedTypedArrayElements());
658   CHECK(!array->IsJSGlobalProxy());
659   JSObject::NormalizeElements(array);
660   return *array;
661 }
662 
663 // GrowArrayElements returns a sentinel Smi if the object was normalized or if
664 // the key is negative.
RUNTIME_FUNCTION(Runtime_GrowArrayElements)665 RUNTIME_FUNCTION(Runtime_GrowArrayElements) {
666   HandleScope scope(isolate);
667   DCHECK_EQ(2, args.length());
668   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
669   CONVERT_NUMBER_CHECKED(int, key, Int32, args[1]);
670 
671   if (key < 0) return Smi::kZero;
672 
673   uint32_t capacity = static_cast<uint32_t>(object->elements()->length());
674   uint32_t index = static_cast<uint32_t>(key);
675 
676   if (index >= capacity) {
677     if (!object->GetElementsAccessor()->GrowCapacity(object, index)) {
678       return Smi::kZero;
679     }
680   }
681 
682   return object->elements();
683 }
684 
685 
RUNTIME_FUNCTION(Runtime_HasComplexElements)686 RUNTIME_FUNCTION(Runtime_HasComplexElements) {
687   HandleScope scope(isolate);
688   DCHECK_EQ(1, args.length());
689   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
690   for (PrototypeIterator iter(isolate, array, kStartAtReceiver);
691        !iter.IsAtEnd(); iter.Advance()) {
692     if (PrototypeIterator::GetCurrent<JSReceiver>(iter)->HasComplexElements()) {
693       return ReadOnlyRoots(isolate).true_value();
694     }
695   }
696   return ReadOnlyRoots(isolate).false_value();
697 }
698 
699 // ES6 22.1.2.2 Array.isArray
RUNTIME_FUNCTION(Runtime_ArrayIsArray)700 RUNTIME_FUNCTION(Runtime_ArrayIsArray) {
701   HandleScope shs(isolate);
702   DCHECK_EQ(1, args.length());
703   CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
704   Maybe<bool> result = Object::IsArray(object);
705   MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
706   return isolate->heap()->ToBoolean(result.FromJust());
707 }
708 
RUNTIME_FUNCTION(Runtime_IsArray)709 RUNTIME_FUNCTION(Runtime_IsArray) {
710   SealHandleScope shs(isolate);
711   DCHECK_EQ(1, args.length());
712   CONVERT_ARG_CHECKED(Object, obj, 0);
713   return isolate->heap()->ToBoolean(obj->IsJSArray());
714 }
715 
RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor)716 RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
717   HandleScope scope(isolate);
718   DCHECK_EQ(1, args.length());
719   CONVERT_ARG_HANDLE_CHECKED(Object, original_array, 0);
720   RETURN_RESULT_OR_FAILURE(
721       isolate, Object::ArraySpeciesConstructor(isolate, original_array));
722 }
723 
724 // ES7 22.1.3.11 Array.prototype.includes
RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow)725 RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) {
726   HandleScope shs(isolate);
727   DCHECK_EQ(3, args.length());
728   CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
729   CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
730 
731   // Let O be ? ToObject(this value).
732   Handle<JSReceiver> object;
733   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
734       isolate, object, Object::ToObject(isolate, handle(args[0], isolate)));
735 
736   // Let len be ? ToLength(? Get(O, "length")).
737   int64_t len;
738   {
739     if (object->map()->instance_type() == JS_ARRAY_TYPE) {
740       uint32_t len32 = 0;
741       bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32);
742       DCHECK(success);
743       USE(success);
744       len = len32;
745     } else {
746       Handle<Object> len_;
747       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
748           isolate, len_,
749           Object::GetProperty(isolate, object,
750                               isolate->factory()->length_string()));
751 
752       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
753                                          Object::ToLength(isolate, len_));
754       len = static_cast<int64_t>(len_->Number());
755       DCHECK_EQ(len, len_->Number());
756     }
757   }
758 
759   if (len == 0) return ReadOnlyRoots(isolate).false_value();
760 
761   // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
762   // produces the value 0.)
763   int64_t index = 0;
764   if (!from_index->IsUndefined(isolate)) {
765     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
766                                        Object::ToInteger(isolate, from_index));
767 
768     if (V8_LIKELY(from_index->IsSmi())) {
769       int start_from = Smi::ToInt(*from_index);
770       if (start_from < 0) {
771         index = std::max<int64_t>(len + start_from, 0);
772       } else {
773         index = start_from;
774       }
775     } else {
776       DCHECK(from_index->IsHeapNumber());
777       double start_from = from_index->Number();
778       if (start_from >= len) return ReadOnlyRoots(isolate).false_value();
779       if (V8_LIKELY(std::isfinite(start_from))) {
780         if (start_from < 0) {
781           index = static_cast<int64_t>(std::max<double>(start_from + len, 0));
782         } else {
783           index = start_from;
784         }
785       }
786     }
787 
788     DCHECK_GE(index, 0);
789   }
790 
791   // If the receiver is not a special receiver type, and the length is a valid
792   // element index, perform fast operation tailored to specific ElementsKinds.
793   if (!object->map()->IsSpecialReceiverMap() && len < kMaxUInt32 &&
794       JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
795     Handle<JSObject> obj = Handle<JSObject>::cast(object);
796     ElementsAccessor* elements = obj->GetElementsAccessor();
797     Maybe<bool> result = elements->IncludesValue(isolate, obj, search_element,
798                                                  static_cast<uint32_t>(index),
799                                                  static_cast<uint32_t>(len));
800     MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
801     return *isolate->factory()->ToBoolean(result.FromJust());
802   }
803 
804   // Otherwise, perform slow lookups for special receiver types
805   for (; index < len; ++index) {
806     // Let elementK be the result of ? Get(O, ! ToString(k)).
807     Handle<Object> element_k;
808     {
809       Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index);
810       bool success;
811       LookupIterator it = LookupIterator::PropertyOrElement(
812           isolate, object, index_obj, &success);
813       DCHECK(success);
814       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
815                                          Object::GetProperty(&it));
816     }
817 
818     // If SameValueZero(searchElement, elementK) is true, return true.
819     if (search_element->SameValueZero(*element_k)) {
820       return ReadOnlyRoots(isolate).true_value();
821     }
822   }
823   return ReadOnlyRoots(isolate).false_value();
824 }
825 
RUNTIME_FUNCTION(Runtime_ArrayIndexOf)826 RUNTIME_FUNCTION(Runtime_ArrayIndexOf) {
827   HandleScope shs(isolate);
828   DCHECK_EQ(3, args.length());
829   CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
830   CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
831 
832   // Let O be ? ToObject(this value).
833   Handle<JSReceiver> object;
834   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
835       isolate, object,
836       Object::ToObject(isolate, args.at(0), "Array.prototype.indexOf"));
837 
838   // Let len be ? ToLength(? Get(O, "length")).
839   int64_t len;
840   {
841     if (object->IsJSArray()) {
842       uint32_t len32 = 0;
843       bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32);
844       DCHECK(success);
845       USE(success);
846       len = len32;
847     } else {
848       Handle<Object> len_;
849       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
850           isolate, len_,
851           Object::GetProperty(isolate, object,
852                               isolate->factory()->length_string()));
853 
854       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
855                                          Object::ToLength(isolate, len_));
856       len = static_cast<int64_t>(len_->Number());
857       DCHECK_EQ(len, len_->Number());
858     }
859   }
860 
861   if (len == 0) return Smi::FromInt(-1);
862 
863   // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
864   // produces the value 0.)
865   int64_t start_from;
866   {
867     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
868                                        Object::ToInteger(isolate, from_index));
869     double fp = from_index->Number();
870     if (fp > len) return Smi::FromInt(-1);
871     if (V8_LIKELY(fp >=
872                   static_cast<double>(std::numeric_limits<int64_t>::min()))) {
873       DCHECK(fp < std::numeric_limits<int64_t>::max());
874       start_from = static_cast<int64_t>(fp);
875     } else {
876       start_from = std::numeric_limits<int64_t>::min();
877     }
878   }
879 
880   int64_t index;
881   if (start_from >= 0) {
882     index = start_from;
883   } else {
884     index = len + start_from;
885     if (index < 0) {
886       index = 0;
887     }
888   }
889 
890   // If the receiver is not a special receiver type, and the length is a valid
891   // element index, perform fast operation tailored to specific ElementsKinds.
892   if (!object->map()->IsSpecialReceiverMap() && len < kMaxUInt32 &&
893       JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
894     Handle<JSObject> obj = Handle<JSObject>::cast(object);
895     ElementsAccessor* elements = obj->GetElementsAccessor();
896     Maybe<int64_t> result = elements->IndexOfValue(isolate, obj, search_element,
897                                                    static_cast<uint32_t>(index),
898                                                    static_cast<uint32_t>(len));
899     MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
900     return *isolate->factory()->NewNumberFromInt64(result.FromJust());
901   }
902 
903   // Otherwise, perform slow lookups for special receiver types
904   for (; index < len; ++index) {
905     // Let elementK be the result of ? Get(O, ! ToString(k)).
906     Handle<Object> element_k;
907     {
908       Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index);
909       bool success;
910       LookupIterator it = LookupIterator::PropertyOrElement(
911           isolate, object, index_obj, &success);
912       DCHECK(success);
913       Maybe<bool> present = JSReceiver::HasProperty(&it);
914       MAYBE_RETURN(present, ReadOnlyRoots(isolate).exception());
915       if (!present.FromJust()) continue;
916       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
917                                          Object::GetProperty(&it));
918       if (search_element->StrictEquals(*element_k)) {
919         return *index_obj;
920       }
921     }
922   }
923   return Smi::FromInt(-1);
924 }
925 
926 }  // namespace internal
927 }  // namespace v8
928