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