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