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