Line data Source code
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/runtime/runtime-utils.h"
6 :
7 : #include "src/arguments.h"
8 : #include "src/code-stubs.h"
9 : #include "src/conversions-inl.h"
10 : #include "src/elements.h"
11 : #include "src/factory.h"
12 : #include "src/isolate-inl.h"
13 : #include "src/keys.h"
14 : #include "src/messages.h"
15 : #include "src/prototype.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 :
20 1485402 : RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
21 742701 : HandleScope scope(isolate);
22 : DCHECK_EQ(2, args.length());
23 1485402 : CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
24 1485402 : CONVERT_ARG_HANDLE_CHECKED(Map, to_map, 1);
25 742701 : ElementsKind to_kind = to_map->elements_kind();
26 742701 : ElementsAccessor::ForKind(to_kind)->TransitionElementsKind(object, to_map);
27 742701 : return *object;
28 : }
29 :
30 : namespace {
31 : // As PrepareElementsForSort, but only on objects where elements is
32 : // a dictionary, and it will stay a dictionary. Collates undefined and
33 : // unexisting elements below limit from position zero of the elements.
34 2078 : Object* PrepareSlowElementsForSort(Handle<JSObject> object, uint32_t limit) {
35 : DCHECK(object->HasDictionaryElements());
36 : Isolate* isolate = object->GetIsolate();
37 : // Must stay in dictionary mode, either because of requires_slow_elements,
38 : // or because we are not going to sort (and therefore compact) all of the
39 : // elements.
40 : Handle<SeededNumberDictionary> dict(object->element_dictionary(), isolate);
41 : Handle<SeededNumberDictionary> new_dict =
42 2078 : SeededNumberDictionary::New(isolate, dict->NumberOfElements());
43 :
44 : uint32_t pos = 0;
45 : uint32_t undefs = 0;
46 : uint32_t max_key = 0;
47 : int capacity = dict->Capacity();
48 : Smi* bailout = Smi::FromInt(-1);
49 : // Entry to the new dictionary does not cause it to grow, as we have
50 : // allocated one that is large enough for all entries.
51 10364 : for (int i = 0; i < capacity; i++) {
52 : Object* k;
53 12462 : if (!dict->ToKey(isolate, i, &k)) continue;
54 :
55 : DCHECK_LE(0, k->Number());
56 : DCHECK_LE(k->Number(), kMaxUInt32);
57 :
58 : HandleScope scope(isolate);
59 4168 : Handle<Object> value(dict->ValueAt(i), isolate);
60 : PropertyDetails details = dict->DetailsAt(i);
61 8307 : if (details.kind() == kAccessor || details.IsReadOnly()) {
62 : // Bail out and do the sorting of undefineds and array holes in JS.
63 : // Also bail out if the element is not supposed to be moved.
64 : return bailout;
65 : }
66 :
67 4139 : uint32_t key = NumberToUint32(k);
68 4139 : if (key < limit) {
69 2109 : if (value->IsUndefined(isolate)) {
70 10 : undefs++;
71 : } else {
72 : Handle<Object> result =
73 2099 : SeededNumberDictionary::Add(new_dict, pos, value, details);
74 : // Add should not grow the dictionary since we allocated the right size.
75 : DCHECK(result.is_identical_to(new_dict));
76 : USE(result);
77 2099 : pos++;
78 : }
79 : } else {
80 : Handle<Object> result =
81 2030 : SeededNumberDictionary::Add(new_dict, key, value, details);
82 : // Add should not grow the dictionary since we allocated the right size.
83 : DCHECK(result.is_identical_to(new_dict));
84 : USE(result);
85 : max_key = Max(max_key, key);
86 : }
87 : }
88 :
89 : uint32_t result = pos;
90 2049 : PropertyDetails no_details = PropertyDetails::Empty();
91 4108 : while (undefs > 0) {
92 10 : if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
93 : // Adding an entry with the key beyond smi-range requires
94 : // allocation. Bailout.
95 : return bailout;
96 : }
97 : HandleScope scope(isolate);
98 : Handle<Object> result = SeededNumberDictionary::Add(
99 10 : new_dict, pos, isolate->factory()->undefined_value(), no_details);
100 : // Add should not grow the dictionary since we allocated the right size.
101 : DCHECK(result.is_identical_to(new_dict));
102 : USE(result);
103 10 : pos++;
104 10 : undefs--;
105 : }
106 2049 : max_key = Max(max_key, pos - 1);
107 :
108 2049 : object->set_elements(*new_dict);
109 2049 : new_dict->UpdateMaxNumberKey(max_key, object);
110 2049 : JSObject::ValidateElements(*object);
111 :
112 4098 : return *isolate->factory()->NewNumberFromUint(result);
113 : }
114 :
115 : // Collects all defined (non-hole) and non-undefined (array) elements at the
116 : // start of the elements array. If the object is in dictionary mode, it is
117 : // converted to fast elements mode. Undefined values are placed after
118 : // non-undefined values. Returns the number of non-undefined values.
119 92778 : Object* PrepareElementsForSort(Handle<JSObject> object, uint32_t limit) {
120 : Isolate* isolate = object->GetIsolate();
121 185536 : if (object->HasSloppyArgumentsElements() || !object->map()->is_extensible()) {
122 : return Smi::FromInt(-1);
123 : }
124 92708 : if (object->HasStringWrapperElements()) {
125 : int len = String::cast(Handle<JSValue>::cast(object)->value())->length();
126 10 : return Smi::FromInt(len);
127 : }
128 :
129 92698 : JSObject::ValidateElements(*object);
130 92698 : if (object->HasDictionaryElements()) {
131 : // Convert to fast elements containing only the existing properties.
132 : // Ordering is irrelevant, since we are going to sort anyway.
133 : Handle<SeededNumberDictionary> dict(object->element_dictionary());
134 4247 : if (object->IsJSArray() || dict->requires_slow_elements() ||
135 40 : dict->max_number_key() >= limit) {
136 2078 : return PrepareSlowElementsForSort(object, limit);
137 : }
138 : // Convert to fast elements.
139 : Handle<Map> new_map =
140 40 : JSObject::GetElementsTransitionMap(object, HOLEY_ELEMENTS);
141 :
142 : PretenureFlag tenure =
143 40 : isolate->heap()->InNewSpace(*object) ? NOT_TENURED : TENURED;
144 : Handle<FixedArray> fast_elements =
145 40 : isolate->factory()->NewFixedArray(dict->NumberOfElements(), tenure);
146 40 : dict->CopyValuesTo(*fast_elements);
147 :
148 40 : JSObject::SetMapAndElements(object, new_map, fast_elements);
149 40 : JSObject::ValidateElements(*object);
150 90580 : } else if (object->HasFixedTypedArrayElements()) {
151 : // Typed arrays cannot have holes or undefined elements.
152 91 : return Smi::FromInt(FixedArrayBase::cast(object->elements())->length());
153 90489 : } else if (!object->HasDoubleElements()) {
154 90452 : JSObject::EnsureWritableFastElements(object);
155 : }
156 : DCHECK(object->HasSmiOrObjectElements() || object->HasDoubleElements());
157 :
158 : // Collect holes at the end, undefined before that and the rest at the
159 : // start, and return the number of non-hole, non-undefined values.
160 :
161 : Handle<FixedArrayBase> elements_base(object->elements());
162 90529 : uint32_t elements_length = static_cast<uint32_t>(elements_base->length());
163 90529 : if (limit > elements_length) {
164 : limit = elements_length;
165 : }
166 90529 : if (limit == 0) {
167 : return Smi::kZero;
168 : }
169 :
170 : uint32_t result = 0;
171 90519 : if (elements_base->map() == isolate->heap()->fixed_double_array_map()) {
172 : FixedDoubleArray* elements = FixedDoubleArray::cast(*elements_base);
173 : // Split elements into defined and the_hole, in that order.
174 : unsigned int holes = limit;
175 : // Assume most arrays contain no holes and undefined values, so minimize the
176 : // number of stores of non-undefined, non-the-hole values.
177 3805 : for (unsigned int i = 0; i < holes; i++) {
178 7536 : if (elements->is_the_hole(i)) {
179 0 : holes--;
180 : } else {
181 : continue;
182 : }
183 : // Position i needs to be filled.
184 0 : while (holes > i) {
185 0 : if (elements->is_the_hole(holes)) {
186 0 : holes--;
187 : } else {
188 : elements->set(i, elements->get_scalar(holes));
189 : break;
190 : }
191 : }
192 : }
193 : result = holes;
194 37 : while (holes < limit) {
195 0 : elements->set_the_hole(holes);
196 0 : holes++;
197 : }
198 : } else {
199 : FixedArray* elements = FixedArray::cast(*elements_base);
200 : DisallowHeapAllocation no_gc;
201 :
202 : // Split elements into defined, undefined and the_hole, in that order. Only
203 : // count locations for undefined and the hole, and fill them afterwards.
204 90482 : WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_gc);
205 : unsigned int undefs = limit;
206 : unsigned int holes = limit;
207 : // Assume most arrays contain no holes and undefined values, so minimize the
208 : // number of stores of non-undefined, non-the-hole values.
209 4464967 : for (unsigned int i = 0; i < undefs; i++) {
210 4374485 : Object* current = elements->get(i);
211 4374485 : if (current->IsTheHole(isolate)) {
212 30941 : holes--;
213 30941 : undefs--;
214 4343544 : } else if (current->IsUndefined(isolate)) {
215 731 : undefs--;
216 : } else {
217 : continue;
218 : }
219 : // Position i needs to be filled.
220 5144609 : while (undefs > i) {
221 5144172 : current = elements->get(undefs);
222 5144172 : if (current->IsTheHole(isolate)) {
223 5112161 : holes--;
224 5112161 : undefs--;
225 32011 : } else if (current->IsUndefined(isolate)) {
226 776 : undefs--;
227 : } else {
228 31235 : elements->set(i, current, write_barrier);
229 31235 : break;
230 : }
231 : }
232 : }
233 : result = undefs;
234 91989 : while (undefs < holes) {
235 1507 : elements->set_undefined(isolate, undefs);
236 1507 : undefs++;
237 : }
238 5233584 : while (holes < limit) {
239 5143102 : elements->set_the_hole(isolate, holes);
240 5143102 : holes++;
241 : }
242 : }
243 :
244 181038 : return *isolate->factory()->NewNumberFromUint(result);
245 : }
246 :
247 : } // namespace
248 :
249 : // Moves all own elements of an object, that are below a limit, to positions
250 : // starting at zero. All undefined values are placed after non-undefined values,
251 : // and are followed by non-existing element. Does not change the length
252 : // property.
253 : // Returns the number of non-undefined elements collected.
254 : // Returns -1 if hole removal is not supported by this method.
255 185576 : RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) {
256 92788 : HandleScope scope(isolate);
257 : DCHECK_EQ(2, args.length());
258 185576 : CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0);
259 185576 : CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
260 92788 : if (object->IsJSProxy()) return Smi::FromInt(-1);
261 92778 : return PrepareElementsForSort(Handle<JSObject>::cast(object), limit);
262 : }
263 :
264 :
265 : // Move contents of argument 0 (an array) to argument 1 (an array)
266 2962 : RUNTIME_FUNCTION(Runtime_MoveArrayContents) {
267 1481 : HandleScope scope(isolate);
268 : DCHECK_EQ(2, args.length());
269 2962 : CONVERT_ARG_HANDLE_CHECKED(JSArray, from, 0);
270 2962 : CONVERT_ARG_HANDLE_CHECKED(JSArray, to, 1);
271 1481 : JSObject::ValidateElements(*from);
272 1481 : JSObject::ValidateElements(*to);
273 :
274 1481 : Handle<FixedArrayBase> new_elements(from->elements());
275 1481 : ElementsKind from_kind = from->GetElementsKind();
276 1481 : Handle<Map> new_map = JSObject::GetElementsTransitionMap(to, from_kind);
277 1481 : JSObject::SetMapAndElements(to, new_map, new_elements);
278 2962 : to->set_length(from->length());
279 :
280 1481 : from->initialize_elements();
281 1481 : from->set_length(Smi::kZero);
282 :
283 1481 : JSObject::ValidateElements(*to);
284 1481 : return *to;
285 : }
286 :
287 :
288 : // How many elements does this object/array have?
289 4516 : RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements) {
290 2258 : DisallowHeapAllocation no_gc;
291 2258 : HandleScope scope(isolate);
292 : DCHECK_EQ(1, args.length());
293 4516 : CONVERT_ARG_CHECKED(JSArray, array, 0);
294 2258 : FixedArrayBase* elements = array->elements();
295 : SealHandleScope shs(isolate);
296 2258 : if (elements->IsDictionary()) {
297 969 : int result = SeededNumberDictionary::cast(elements)->NumberOfElements();
298 969 : return Smi::FromInt(result);
299 : } else {
300 : DCHECK(array->length()->IsSmi());
301 : // For packed elements, we know the exact number of elements
302 1289 : int length = elements->length();
303 1289 : ElementsKind kind = array->GetElementsKind();
304 1289 : if (IsFastPackedElementsKind(kind)) {
305 280 : return Smi::FromInt(length);
306 : }
307 : // For holey elements, take samples from the buffer checking for holes
308 : // to generate the estimate.
309 : const int kNumberOfHoleCheckSamples = 97;
310 : int increment = (length < kNumberOfHoleCheckSamples)
311 : ? 1
312 1009 : : static_cast<int>(length / kNumberOfHoleCheckSamples);
313 1009 : ElementsAccessor* accessor = array->GetElementsAccessor();
314 : int holes = 0;
315 99561 : for (int i = 0; i < length; i += increment) {
316 99561 : if (!accessor->HasElement(array, i, elements)) {
317 43172 : ++holes;
318 : }
319 : }
320 1009 : int estimate = static_cast<int>((kNumberOfHoleCheckSamples - holes) /
321 1009 : kNumberOfHoleCheckSamples * length);
322 1009 : return Smi::FromInt(estimate);
323 2258 : }
324 : }
325 :
326 :
327 : // Returns an array that tells you where in the [0, length) interval an array
328 : // might have elements. Can either return an array of keys (positive integers
329 : // or undefined) or a number representing the positive length of an interval
330 : // starting at index 0.
331 : // Intervals can span over some keys that are not in the object.
332 11706 : RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
333 5853 : HandleScope scope(isolate);
334 : DCHECK_EQ(2, args.length());
335 11706 : CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
336 11706 : CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
337 5853 : ElementsKind kind = array->GetElementsKind();
338 :
339 5853 : if (IsFastElementsKind(kind) || IsFixedTypedArrayElementsKind(kind)) {
340 3358 : uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
341 6716 : return *isolate->factory()->NewNumberFromUint(Min(actual_length, length));
342 : }
343 :
344 2495 : if (kind == FAST_STRING_WRAPPER_ELEMENTS) {
345 : int string_length =
346 20 : String::cast(Handle<JSValue>::cast(array)->value())->length();
347 10 : int backing_store_length = array->elements()->length();
348 : return *isolate->factory()->NewNumberFromUint(
349 : Min(length,
350 20 : static_cast<uint32_t>(Max(string_length, backing_store_length))));
351 : }
352 :
353 : KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
354 4970 : ALL_PROPERTIES);
355 12305 : for (PrototypeIterator iter(isolate, array, kStartAtReceiver);
356 7335 : !iter.IsAtEnd(); iter.Advance()) {
357 7335 : Handle<JSReceiver> current(PrototypeIterator::GetCurrent<JSReceiver>(iter));
358 7335 : if (current->HasComplexElements()) {
359 0 : return *isolate->factory()->NewNumberFromUint(length);
360 : }
361 : accumulator.CollectOwnElementIndices(array,
362 14670 : Handle<JSObject>::cast(current));
363 : }
364 : // Erase any keys >= length.
365 : Handle<FixedArray> keys =
366 2485 : accumulator.GetKeys(GetKeysConversion::kKeepNumbers);
367 : int j = 0;
368 25103 : for (int i = 0; i < keys->length(); i++) {
369 11309 : if (NumberToUint32(keys->get(i)) >= length) continue;
370 10759 : if (i != j) keys->set(j, keys->get(i));
371 10519 : j++;
372 : }
373 :
374 2485 : if (j != keys->length()) {
375 360 : isolate->heap()->RightTrimFixedArray(*keys, keys->length() - j);
376 : }
377 :
378 10823 : return *isolate->factory()->NewJSArrayWithElements(keys);
379 : }
380 :
381 1004022 : RUNTIME_FUNCTION(Runtime_NewArray) {
382 502011 : HandleScope scope(isolate);
383 : DCHECK_LE(3, args.length());
384 502011 : int const argc = args.length() - 3;
385 : // TODO(bmeurer): Remove this Arguments nonsense.
386 502011 : Arguments argv(argc, args.arguments() - 1);
387 1004022 : CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0);
388 1004022 : CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, argc + 1);
389 1004022 : CONVERT_ARG_HANDLE_CHECKED(HeapObject, type_info, argc + 2);
390 : // TODO(bmeurer): Use MaybeHandle to pass around the AllocationSite.
391 : Handle<AllocationSite> site = type_info->IsAllocationSite()
392 : ? Handle<AllocationSite>::cast(type_info)
393 502011 : : Handle<AllocationSite>::null();
394 :
395 502011 : Factory* factory = isolate->factory();
396 :
397 : // If called through new, new.target can be:
398 : // - a subclass of constructor,
399 : // - a proxy wrapper around constructor, or
400 : // - the constructor itself.
401 : // If called through Reflect.construct, it's guaranteed to be a constructor by
402 : // REFLECT_CONSTRUCT_PREPARE.
403 : DCHECK(new_target->IsConstructor());
404 :
405 : bool holey = false;
406 502011 : bool can_use_type_feedback = !site.is_null();
407 : bool can_inline_array_constructor = true;
408 502011 : if (argv.length() == 1) {
409 5003 : Handle<Object> argument_one = argv.at<Object>(0);
410 5003 : if (argument_one->IsSmi()) {
411 6722 : int value = Handle<Smi>::cast(argument_one)->value();
412 6683 : if (value < 0 ||
413 3322 : JSArray::SetLengthWouldNormalize(isolate->heap(), value)) {
414 : // the array is a dictionary in this case.
415 : can_use_type_feedback = false;
416 3036 : } else if (value != 0) {
417 : holey = true;
418 2419 : if (value >= JSArray::kInitialMaxFastElementArray) {
419 : can_inline_array_constructor = false;
420 : }
421 : }
422 : } else {
423 : // Non-smi length argument produces a dictionary
424 : can_use_type_feedback = false;
425 : }
426 : }
427 :
428 : Handle<Map> initial_map;
429 1004022 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
430 : isolate, initial_map,
431 : JSFunction::GetDerivedMap(isolate, constructor, new_target));
432 :
433 : ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind()
434 1004022 : : initial_map->elements_kind();
435 502011 : if (holey && !IsHoleyElementsKind(to_kind)) {
436 761 : to_kind = GetHoleyElementsKind(to_kind);
437 : // Update the allocation site info to reflect the advice alteration.
438 761 : if (!site.is_null()) site->SetElementsKind(to_kind);
439 : }
440 :
441 : // We should allocate with an initial map that reflects the allocation site
442 : // advice. Therefore we use AllocateJSObjectFromMap instead of passing
443 : // the constructor.
444 502011 : if (to_kind != initial_map->elements_kind()) {
445 329713 : initial_map = Map::AsElementsKind(initial_map, to_kind);
446 : }
447 :
448 : // If we don't care to track arrays of to_kind ElementsKind, then
449 : // don't emit a memento for them.
450 : Handle<AllocationSite> allocation_site;
451 502011 : if (AllocationSite::ShouldTrack(to_kind)) {
452 173861 : allocation_site = site;
453 : }
454 :
455 : Handle<JSArray> array = Handle<JSArray>::cast(
456 502011 : factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site));
457 :
458 502011 : factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
459 :
460 502011 : ElementsKind old_kind = array->GetElementsKind();
461 502011 : RETURN_FAILURE_ON_EXCEPTION(isolate,
462 : ArrayConstructInitializeElements(array, &argv));
463 501893 : if (!site.is_null()) {
464 677306 : if ((old_kind != array->GetElementsKind() || !can_use_type_feedback ||
465 338174 : !can_inline_array_constructor)) {
466 : // The arguments passed in caused a transition. This kind of complexity
467 : // can't be dealt with in the inlined hydrogen array constructor case.
468 : // We must mark the allocationsite as un-inlinable.
469 2356 : site->SetDoNotInlineCall();
470 : }
471 : } else {
472 162761 : if (old_kind != array->GetElementsKind() || !can_inline_array_constructor) {
473 : // We don't have an AllocationSite for this Array constructor invocation,
474 : // i.e. it might a call from Array#map or from an Array subclass, so we
475 : // just flip the bit on the global protector cell instead.
476 : // TODO(bmeurer): Find a better way to mark this. Global protectors
477 : // tend to back-fire over time...
478 2301 : if (isolate->IsArrayConstructorIntact()) {
479 216 : isolate->InvalidateArrayConstructorProtector();
480 : }
481 : }
482 : }
483 :
484 502011 : return *array;
485 : }
486 :
487 5982 : RUNTIME_FUNCTION(Runtime_NormalizeElements) {
488 2991 : HandleScope scope(isolate);
489 : DCHECK_EQ(1, args.length());
490 5982 : CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
491 2991 : CHECK(!array->HasFixedTypedArrayElements());
492 2991 : CHECK(!array->IsJSGlobalProxy());
493 2991 : JSObject::NormalizeElements(array);
494 2991 : return *array;
495 : }
496 :
497 :
498 : // GrowArrayElements returns a sentinel Smi if the object was normalized.
499 712 : RUNTIME_FUNCTION(Runtime_GrowArrayElements) {
500 356 : HandleScope scope(isolate);
501 : DCHECK_EQ(2, args.length());
502 712 : CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
503 712 : CONVERT_NUMBER_CHECKED(int, key, Int32, args[1]);
504 :
505 356 : if (key < 0) {
506 0 : return object->elements();
507 : }
508 :
509 356 : uint32_t capacity = static_cast<uint32_t>(object->elements()->length());
510 356 : uint32_t index = static_cast<uint32_t>(key);
511 :
512 356 : if (index >= capacity) {
513 356 : if (!object->GetElementsAccessor()->GrowCapacity(object, index)) {
514 : return Smi::kZero;
515 : }
516 : }
517 :
518 : // On success, return the fixed array elements.
519 356 : return object->elements();
520 : }
521 :
522 :
523 6262 : RUNTIME_FUNCTION(Runtime_HasComplexElements) {
524 3131 : HandleScope scope(isolate);
525 : DCHECK_EQ(1, args.length());
526 6262 : CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
527 15455 : for (PrototypeIterator iter(isolate, array, kStartAtReceiver);
528 9193 : !iter.IsAtEnd(); iter.Advance()) {
529 18496 : if (PrototypeIterator::GetCurrent<JSReceiver>(iter)->HasComplexElements()) {
530 55 : return isolate->heap()->true_value();
531 : }
532 : }
533 3076 : return isolate->heap()->false_value();
534 : }
535 :
536 : // ES6 22.1.2.2 Array.isArray
537 876 : RUNTIME_FUNCTION(Runtime_ArrayIsArray) {
538 438 : HandleScope shs(isolate);
539 : DCHECK_EQ(1, args.length());
540 438 : CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
541 : Maybe<bool> result = Object::IsArray(object);
542 438 : MAYBE_RETURN(result, isolate->heap()->exception());
543 392 : return isolate->heap()->ToBoolean(result.FromJust());
544 : }
545 :
546 36 : RUNTIME_FUNCTION(Runtime_IsArray) {
547 : SealHandleScope shs(isolate);
548 : DCHECK_EQ(1, args.length());
549 18 : CONVERT_ARG_CHECKED(Object, obj, 0);
550 18 : return isolate->heap()->ToBoolean(obj->IsJSArray());
551 : }
552 :
553 10252 : RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
554 5126 : HandleScope scope(isolate);
555 : DCHECK_EQ(1, args.length());
556 5126 : CONVERT_ARG_HANDLE_CHECKED(Object, original_array, 0);
557 10252 : RETURN_RESULT_OR_FAILURE(
558 5126 : isolate, Object::ArraySpeciesConstructor(isolate, original_array));
559 : }
560 :
561 : // ES7 22.1.3.11 Array.prototype.includes
562 9740 : RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) {
563 4870 : HandleScope shs(isolate);
564 : DCHECK_EQ(3, args.length());
565 4870 : CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
566 4870 : CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
567 :
568 : // Let O be ? ToObject(this value).
569 : Handle<JSReceiver> object;
570 14610 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
571 : isolate, object, Object::ToObject(isolate, handle(args[0], isolate)));
572 :
573 : // Let len be ? ToLength(? Get(O, "length")).
574 : int64_t len;
575 : {
576 4852 : if (object->map()->instance_type() == JS_ARRAY_TYPE) {
577 207 : uint32_t len32 = 0;
578 207 : bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32);
579 : DCHECK(success);
580 : USE(success);
581 207 : len = len32;
582 : } else {
583 : Handle<Object> len_;
584 13935 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
585 : isolate, len_,
586 : Object::GetProperty(object, isolate->factory()->length_string()));
587 :
588 9272 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
589 : Object::ToLength(isolate, len_));
590 4618 : len = static_cast<int64_t>(len_->Number());
591 : DCHECK_EQ(len, len_->Number());
592 : }
593 : }
594 :
595 4825 : if (len == 0) return isolate->heap()->false_value();
596 :
597 : // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
598 : // produces the value 0.)
599 : int64_t index = 0;
600 4483 : if (!from_index->IsUndefined(isolate)) {
601 2758 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
602 : Object::ToInteger(isolate, from_index));
603 :
604 1361 : if (V8_LIKELY(from_index->IsSmi())) {
605 1307 : int start_from = Smi::ToInt(*from_index);
606 1307 : if (start_from < 0) {
607 207 : index = std::max<int64_t>(len + start_from, 0);
608 : } else {
609 1100 : index = start_from;
610 : }
611 : } else {
612 : DCHECK(from_index->IsHeapNumber());
613 54 : double start_from = from_index->Number();
614 54 : if (start_from >= len) return isolate->heap()->false_value();
615 36 : if (V8_LIKELY(std::isfinite(start_from))) {
616 27 : if (start_from < 0) {
617 0 : index = static_cast<int64_t>(std::max<double>(start_from + len, 0));
618 : } else {
619 27 : index = start_from;
620 : }
621 : }
622 : }
623 :
624 : DCHECK_GE(index, 0);
625 : }
626 :
627 : // If the receiver is not a special receiver type, and the length is a valid
628 : // element index, perform fast operation tailored to specific ElementsKinds.
629 8858 : if (!object->map()->IsSpecialReceiverMap() && len < kMaxUInt32 &&
630 4411 : JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
631 1902 : Handle<JSObject> obj = Handle<JSObject>::cast(object);
632 1902 : ElementsAccessor* elements = obj->GetElementsAccessor();
633 : Maybe<bool> result = elements->IncludesValue(isolate, obj, search_element,
634 : static_cast<uint32_t>(index),
635 1902 : static_cast<uint32_t>(len));
636 1902 : MAYBE_RETURN(result, isolate->heap()->exception());
637 3786 : return *isolate->factory()->ToBoolean(result.FromJust());
638 : }
639 :
640 : // Otherwise, perform slow lookups for special receiver types
641 11669 : for (; index < len; ++index) {
642 : // Let elementK be the result of ? Get(O, ! ToString(k)).
643 : Handle<Object> element_k;
644 : {
645 11886 : Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index);
646 : bool success;
647 : LookupIterator it = LookupIterator::PropertyOrElement(
648 11886 : isolate, object, index_obj, &success);
649 : DCHECK(success);
650 23772 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
651 : Object::GetProperty(&it));
652 : }
653 :
654 : // If SameValueZero(searchElement, elementK) is true, return true.
655 11886 : if (search_element->SameValueZero(*element_k)) {
656 217 : return isolate->heap()->true_value();
657 : }
658 : }
659 2328 : return isolate->heap()->false_value();
660 : }
661 :
662 14658 : RUNTIME_FUNCTION(Runtime_ArrayIndexOf) {
663 7329 : HandleScope shs(isolate);
664 : DCHECK_EQ(3, args.length());
665 7329 : CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
666 7329 : CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
667 :
668 : // Let O be ? ToObject(this value).
669 : Handle<JSReceiver> object;
670 14658 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
671 : isolate, object,
672 : Object::ToObject(isolate, args.at(0), "Array.prototype.indexOf"));
673 :
674 : // Let len be ? ToLength(? Get(O, "length")).
675 : int64_t len;
676 : {
677 7140 : if (object->IsJSArray()) {
678 5055 : uint32_t len32 = 0;
679 5055 : bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32);
680 : DCHECK(success);
681 : USE(success);
682 5055 : len = len32;
683 : } else {
684 : Handle<Object> len_;
685 6255 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
686 : isolate, len_,
687 : Object::GetProperty(object, isolate->factory()->length_string()));
688 :
689 4152 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
690 : Object::ToLength(isolate, len_));
691 2076 : len = static_cast<int64_t>(len_->Number());
692 : DCHECK_EQ(len, len_->Number());
693 : }
694 : }
695 :
696 7131 : if (len == 0) return Smi::FromInt(-1);
697 :
698 : // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
699 : // produces the value 0.)
700 : int64_t start_from;
701 : {
702 12648 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
703 : Object::ToInteger(isolate, from_index));
704 6324 : double fp = from_index->Number();
705 6324 : if (fp > len) return Smi::FromInt(-1);
706 6324 : if (V8_LIKELY(fp >=
707 : static_cast<double>(std::numeric_limits<int64_t>::min()))) {
708 : DCHECK(fp < std::numeric_limits<int64_t>::max());
709 6324 : start_from = static_cast<int64_t>(fp);
710 : } else {
711 : start_from = std::numeric_limits<int64_t>::min();
712 : }
713 : }
714 :
715 : int64_t index;
716 6324 : if (start_from >= 0) {
717 : index = start_from;
718 : } else {
719 279 : index = len + start_from;
720 279 : if (index < 0) {
721 : index = 0;
722 : }
723 : }
724 :
725 : // If the receiver is not a special receiver type, and the length is a valid
726 : // element index, perform fast operation tailored to specific ElementsKinds.
727 12610 : if (!object->map()->IsSpecialReceiverMap() && len < kMaxUInt32 &&
728 6286 : JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
729 6101 : Handle<JSObject> obj = Handle<JSObject>::cast(object);
730 6101 : ElementsAccessor* elements = obj->GetElementsAccessor();
731 : Maybe<int64_t> result = elements->IndexOfValue(isolate, obj, search_element,
732 : static_cast<uint32_t>(index),
733 6101 : static_cast<uint32_t>(len));
734 6101 : MAYBE_RETURN(result, isolate->heap()->exception());
735 12184 : return *isolate->factory()->NewNumberFromInt64(result.FromJust());
736 : }
737 :
738 : // Otherwise, perform slow lookups for special receiver types
739 568 : for (; index < len; ++index) {
740 : // Let elementK be the result of ? Get(O, ! ToString(k)).
741 : Handle<Object> element_k;
742 : {
743 703 : Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index);
744 : bool success;
745 : LookupIterator it = LookupIterator::PropertyOrElement(
746 703 : isolate, object, index_obj, &success);
747 : DCHECK(success);
748 703 : Maybe<bool> present = JSReceiver::HasProperty(&it);
749 838 : MAYBE_RETURN(present, isolate->heap()->exception());
750 811 : if (!present.FromJust()) continue;
751 1150 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
752 : Object::GetProperty(&it));
753 575 : if (search_element->StrictEquals(*element_k)) {
754 : return *index_obj;
755 : }
756 : }
757 : }
758 88 : return Smi::FromInt(-1);
759 : }
760 :
761 :
762 0 : RUNTIME_FUNCTION(Runtime_SpreadIterablePrepare) {
763 0 : HandleScope scope(isolate);
764 : DCHECK_EQ(1, args.length());
765 0 : CONVERT_ARG_HANDLE_CHECKED(Object, spread, 0);
766 :
767 : // Iterate over the spread if we need to.
768 0 : if (spread->IterationHasObservableEffects()) {
769 0 : Handle<JSFunction> spread_iterable_function = isolate->spread_iterable();
770 0 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
771 : isolate, spread,
772 : Execution::Call(isolate, spread_iterable_function,
773 : isolate->factory()->undefined_value(), 1, &spread));
774 : }
775 :
776 0 : return *spread;
777 : }
778 :
779 : } // namespace internal
780 : } // namespace v8
|