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 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 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 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 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 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 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 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) 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? 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. 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 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 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 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. 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 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 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 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 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 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 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