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 RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) { 21 HandleScope scope(isolate); 22 DCHECK(args.length() == 1); 23 CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0); 24 Object* length = prototype->length(); 25 CHECK(length->IsSmi()); 26 CHECK(Smi::cast(length)->value() == 0); 27 CHECK(prototype->HasFastSmiOrObjectElements()); 28 // This is necessary to enable fast checks for absence of elements 29 // on Array.prototype and below. 30 prototype->set_elements(isolate->heap()->empty_fixed_array()); 31 return Smi::kZero; 32 } 33 34 static void InstallCode( 35 Isolate* isolate, Handle<JSObject> holder, const char* name, 36 Handle<Code> code, int argc = -1, 37 BuiltinFunctionId id = static_cast<BuiltinFunctionId>(-1)) { 38 Handle<String> key = isolate->factory()->InternalizeUtf8String(name); 39 Handle<JSFunction> optimized = 40 isolate->factory()->NewFunctionWithoutPrototype(key, code); 41 if (argc < 0) { 42 optimized->shared()->DontAdaptArguments(); 43 } else { 44 optimized->shared()->set_internal_formal_parameter_count(argc); 45 } 46 if (id >= 0) { 47 optimized->shared()->set_builtin_function_id(id); 48 } 49 JSObject::AddProperty(holder, key, optimized, NONE); 50 } 51 52 static void InstallBuiltin( 53 Isolate* isolate, Handle<JSObject> holder, const char* name, 54 Builtins::Name builtin_name, int argc = -1, 55 BuiltinFunctionId id = static_cast<BuiltinFunctionId>(-1)) { 56 InstallCode(isolate, holder, name, 57 handle(isolate->builtins()->builtin(builtin_name), isolate), argc, 58 id); 59 } 60 61 RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) { 62 HandleScope scope(isolate); 63 DCHECK(args.length() == 0); 64 Handle<JSObject> holder = 65 isolate->factory()->NewJSObject(isolate->object_function()); 66 67 InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop); 68 if (FLAG_minimal) { 69 InstallBuiltin(isolate, holder, "push", Builtins::kArrayPush); 70 } else { 71 FastArrayPushStub stub(isolate); 72 InstallCode(isolate, holder, "push", stub.GetCode()); 73 } 74 InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift); 75 InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift); 76 InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice); 77 InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice); 78 InstallBuiltin(isolate, holder, "includes", Builtins::kArrayIncludes, 2); 79 InstallBuiltin(isolate, holder, "indexOf", Builtins::kArrayIndexOf, 2); 80 InstallBuiltin(isolate, holder, "keys", Builtins::kArrayPrototypeKeys, 0, 81 kArrayKeys); 82 InstallBuiltin(isolate, holder, "values", Builtins::kArrayPrototypeValues, 0, 83 kArrayValues); 84 InstallBuiltin(isolate, holder, "entries", Builtins::kArrayPrototypeEntries, 85 0, kArrayEntries); 86 87 return *holder; 88 } 89 90 91 RUNTIME_FUNCTION(Runtime_FixedArrayGet) { 92 SealHandleScope shs(isolate); 93 DCHECK(args.length() == 2); 94 CONVERT_ARG_CHECKED(FixedArray, object, 0); 95 CONVERT_SMI_ARG_CHECKED(index, 1); 96 return object->get(index); 97 } 98 99 100 RUNTIME_FUNCTION(Runtime_FixedArraySet) { 101 SealHandleScope shs(isolate); 102 DCHECK(args.length() == 3); 103 CONVERT_ARG_CHECKED(FixedArray, object, 0); 104 CONVERT_SMI_ARG_CHECKED(index, 1); 105 CONVERT_ARG_CHECKED(Object, value, 2); 106 object->set(index, value); 107 return isolate->heap()->undefined_value(); 108 } 109 110 111 RUNTIME_FUNCTION(Runtime_TransitionElementsKind) { 112 HandleScope scope(isolate); 113 DCHECK_EQ(2, args.length()); 114 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); 115 CONVERT_ARG_HANDLE_CHECKED(Map, to_map, 1); 116 ElementsKind to_kind = to_map->elements_kind(); 117 ElementsAccessor::ForKind(to_kind)->TransitionElementsKind(object, to_map); 118 return *object; 119 } 120 121 122 // Moves all own elements of an object, that are below a limit, to positions 123 // starting at zero. All undefined values are placed after non-undefined values, 124 // and are followed by non-existing element. Does not change the length 125 // property. 126 // Returns the number of non-undefined elements collected. 127 // Returns -1 if hole removal is not supported by this method. 128 RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) { 129 HandleScope scope(isolate); 130 DCHECK(args.length() == 2); 131 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0); 132 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]); 133 if (object->IsJSProxy()) return Smi::FromInt(-1); 134 return *JSObject::PrepareElementsForSort(Handle<JSObject>::cast(object), 135 limit); 136 } 137 138 139 // Move contents of argument 0 (an array) to argument 1 (an array) 140 RUNTIME_FUNCTION(Runtime_MoveArrayContents) { 141 HandleScope scope(isolate); 142 DCHECK(args.length() == 2); 143 CONVERT_ARG_HANDLE_CHECKED(JSArray, from, 0); 144 CONVERT_ARG_HANDLE_CHECKED(JSArray, to, 1); 145 JSObject::ValidateElements(from); 146 JSObject::ValidateElements(to); 147 148 Handle<FixedArrayBase> new_elements(from->elements()); 149 ElementsKind from_kind = from->GetElementsKind(); 150 Handle<Map> new_map = JSObject::GetElementsTransitionMap(to, from_kind); 151 JSObject::SetMapAndElements(to, new_map, new_elements); 152 to->set_length(from->length()); 153 154 JSObject::ResetElements(from); 155 from->set_length(Smi::kZero); 156 157 JSObject::ValidateElements(to); 158 return *to; 159 } 160 161 162 // How many elements does this object/array have? 163 RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements) { 164 HandleScope scope(isolate); 165 DCHECK(args.length() == 1); 166 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0); 167 Handle<FixedArrayBase> elements(array->elements(), isolate); 168 SealHandleScope shs(isolate); 169 if (elements->IsDictionary()) { 170 int result = 171 Handle<SeededNumberDictionary>::cast(elements)->NumberOfElements(); 172 return Smi::FromInt(result); 173 } else { 174 DCHECK(array->length()->IsSmi()); 175 // For packed elements, we know the exact number of elements 176 int length = elements->length(); 177 ElementsKind kind = array->GetElementsKind(); 178 if (IsFastPackedElementsKind(kind)) { 179 return Smi::FromInt(length); 180 } 181 // For holey elements, take samples from the buffer checking for holes 182 // to generate the estimate. 183 const int kNumberOfHoleCheckSamples = 97; 184 int increment = (length < kNumberOfHoleCheckSamples) 185 ? 1 186 : static_cast<int>(length / kNumberOfHoleCheckSamples); 187 ElementsAccessor* accessor = array->GetElementsAccessor(); 188 int holes = 0; 189 for (int i = 0; i < length; i += increment) { 190 if (!accessor->HasElement(array, i, elements)) { 191 ++holes; 192 } 193 } 194 int estimate = static_cast<int>((kNumberOfHoleCheckSamples - holes) / 195 kNumberOfHoleCheckSamples * length); 196 return Smi::FromInt(estimate); 197 } 198 } 199 200 201 // Returns an array that tells you where in the [0, length) interval an array 202 // might have elements. Can either return an array of keys (positive integers 203 // or undefined) or a number representing the positive length of an interval 204 // starting at index 0. 205 // Intervals can span over some keys that are not in the object. 206 RUNTIME_FUNCTION(Runtime_GetArrayKeys) { 207 HandleScope scope(isolate); 208 DCHECK(args.length() == 2); 209 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0); 210 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]); 211 ElementsKind kind = array->GetElementsKind(); 212 213 if (IsFastElementsKind(kind) || IsFixedTypedArrayElementsKind(kind)) { 214 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length()); 215 return *isolate->factory()->NewNumberFromUint(Min(actual_length, length)); 216 } 217 218 if (kind == FAST_STRING_WRAPPER_ELEMENTS) { 219 int string_length = 220 String::cast(Handle<JSValue>::cast(array)->value())->length(); 221 int backing_store_length = array->elements()->length(); 222 return *isolate->factory()->NewNumberFromUint( 223 Min(length, 224 static_cast<uint32_t>(Max(string_length, backing_store_length)))); 225 } 226 227 KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly, 228 ALL_PROPERTIES); 229 for (PrototypeIterator iter(isolate, array, kStartAtReceiver); 230 !iter.IsAtEnd(); iter.Advance()) { 231 if (PrototypeIterator::GetCurrent(iter)->IsJSProxy() || 232 PrototypeIterator::GetCurrent<JSObject>(iter) 233 ->HasIndexedInterceptor()) { 234 // Bail out if we find a proxy or interceptor, likely not worth 235 // collecting keys in that case. 236 return *isolate->factory()->NewNumberFromUint(length); 237 } 238 Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter); 239 accumulator.CollectOwnElementIndices(array, current); 240 } 241 // Erase any keys >= length. 242 Handle<FixedArray> keys = 243 accumulator.GetKeys(GetKeysConversion::kKeepNumbers); 244 int j = 0; 245 for (int i = 0; i < keys->length(); i++) { 246 if (NumberToUint32(keys->get(i)) >= length) continue; 247 if (i != j) keys->set(j, keys->get(i)); 248 j++; 249 } 250 251 if (j != keys->length()) { 252 isolate->heap()->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>( 253 *keys, keys->length() - j); 254 } 255 256 return *isolate->factory()->NewJSArrayWithElements(keys); 257 } 258 259 260 namespace { 261 262 Object* ArrayConstructorCommon(Isolate* isolate, Handle<JSFunction> constructor, 263 Handle<JSReceiver> new_target, 264 Handle<AllocationSite> site, 265 Arguments* caller_args) { 266 Factory* factory = isolate->factory(); 267 268 // If called through new, new.target can be: 269 // - a subclass of constructor, 270 // - a proxy wrapper around constructor, or 271 // - the constructor itself. 272 // If called through Reflect.construct, it's guaranteed to be a constructor by 273 // REFLECT_CONSTRUCT_PREPARE. 274 DCHECK(new_target->IsConstructor()); 275 276 bool holey = false; 277 bool can_use_type_feedback = !site.is_null(); 278 bool can_inline_array_constructor = true; 279 if (caller_args->length() == 1) { 280 Handle<Object> argument_one = caller_args->at<Object>(0); 281 if (argument_one->IsSmi()) { 282 int value = Handle<Smi>::cast(argument_one)->value(); 283 if (value < 0 || 284 JSArray::SetLengthWouldNormalize(isolate->heap(), value)) { 285 // the array is a dictionary in this case. 286 can_use_type_feedback = false; 287 } else if (value != 0) { 288 holey = true; 289 if (value >= JSArray::kInitialMaxFastElementArray) { 290 can_inline_array_constructor = false; 291 } 292 } 293 } else { 294 // Non-smi length argument produces a dictionary 295 can_use_type_feedback = false; 296 } 297 } 298 299 Handle<Map> initial_map; 300 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 301 isolate, initial_map, 302 JSFunction::GetDerivedMap(isolate, constructor, new_target)); 303 304 ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind() 305 : initial_map->elements_kind(); 306 if (holey && !IsFastHoleyElementsKind(to_kind)) { 307 to_kind = GetHoleyElementsKind(to_kind); 308 // Update the allocation site info to reflect the advice alteration. 309 if (!site.is_null()) site->SetElementsKind(to_kind); 310 } 311 312 // We should allocate with an initial map that reflects the allocation site 313 // advice. Therefore we use AllocateJSObjectFromMap instead of passing 314 // the constructor. 315 if (to_kind != initial_map->elements_kind()) { 316 initial_map = Map::AsElementsKind(initial_map, to_kind); 317 } 318 319 // If we don't care to track arrays of to_kind ElementsKind, then 320 // don't emit a memento for them. 321 Handle<AllocationSite> allocation_site; 322 if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) { 323 allocation_site = site; 324 } 325 326 Handle<JSArray> array = Handle<JSArray>::cast( 327 factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site)); 328 329 factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS); 330 331 ElementsKind old_kind = array->GetElementsKind(); 332 RETURN_FAILURE_ON_EXCEPTION( 333 isolate, ArrayConstructInitializeElements(array, caller_args)); 334 if (!site.is_null() && 335 (old_kind != array->GetElementsKind() || !can_use_type_feedback || 336 !can_inline_array_constructor)) { 337 // The arguments passed in caused a transition. This kind of complexity 338 // can't be dealt with in the inlined hydrogen array constructor case. 339 // We must mark the allocationsite as un-inlinable. 340 site->SetDoNotInlineCall(); 341 } 342 343 return *array; 344 } 345 346 } // namespace 347 348 RUNTIME_FUNCTION(Runtime_NewArray) { 349 HandleScope scope(isolate); 350 DCHECK_LE(3, args.length()); 351 int const argc = args.length() - 3; 352 // TODO(bmeurer): Remove this Arguments nonsense. 353 Arguments argv(argc, args.arguments() - 1); 354 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0); 355 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, argc + 1); 356 CONVERT_ARG_HANDLE_CHECKED(HeapObject, type_info, argc + 2); 357 // TODO(bmeurer): Use MaybeHandle to pass around the AllocationSite. 358 Handle<AllocationSite> site = type_info->IsAllocationSite() 359 ? Handle<AllocationSite>::cast(type_info) 360 : Handle<AllocationSite>::null(); 361 return ArrayConstructorCommon(isolate, constructor, new_target, site, &argv); 362 } 363 364 RUNTIME_FUNCTION(Runtime_NormalizeElements) { 365 HandleScope scope(isolate); 366 DCHECK(args.length() == 1); 367 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0); 368 CHECK(!array->HasFixedTypedArrayElements()); 369 CHECK(!array->IsJSGlobalProxy()); 370 JSObject::NormalizeElements(array); 371 return *array; 372 } 373 374 375 // GrowArrayElements returns a sentinel Smi if the object was normalized. 376 RUNTIME_FUNCTION(Runtime_GrowArrayElements) { 377 HandleScope scope(isolate); 378 DCHECK(args.length() == 2); 379 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); 380 CONVERT_NUMBER_CHECKED(int, key, Int32, args[1]); 381 382 if (key < 0) { 383 return object->elements(); 384 } 385 386 uint32_t capacity = static_cast<uint32_t>(object->elements()->length()); 387 uint32_t index = static_cast<uint32_t>(key); 388 389 if (index >= capacity) { 390 if (!object->GetElementsAccessor()->GrowCapacity(object, index)) { 391 return Smi::kZero; 392 } 393 } 394 395 // On success, return the fixed array elements. 396 return object->elements(); 397 } 398 399 400 RUNTIME_FUNCTION(Runtime_HasComplexElements) { 401 HandleScope scope(isolate); 402 DCHECK(args.length() == 1); 403 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0); 404 for (PrototypeIterator iter(isolate, array, kStartAtReceiver); 405 !iter.IsAtEnd(); iter.Advance()) { 406 if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { 407 return isolate->heap()->true_value(); 408 } 409 Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter); 410 if (current->HasIndexedInterceptor()) { 411 return isolate->heap()->true_value(); 412 } 413 if (!current->HasDictionaryElements()) continue; 414 if (current->element_dictionary()->HasComplexElements()) { 415 return isolate->heap()->true_value(); 416 } 417 } 418 return isolate->heap()->false_value(); 419 } 420 421 // ES6 22.1.2.2 Array.isArray 422 RUNTIME_FUNCTION(Runtime_ArrayIsArray) { 423 HandleScope shs(isolate); 424 DCHECK(args.length() == 1); 425 CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); 426 Maybe<bool> result = Object::IsArray(object); 427 MAYBE_RETURN(result, isolate->heap()->exception()); 428 return isolate->heap()->ToBoolean(result.FromJust()); 429 } 430 431 RUNTIME_FUNCTION(Runtime_IsArray) { 432 SealHandleScope shs(isolate); 433 DCHECK(args.length() == 1); 434 CONVERT_ARG_CHECKED(Object, obj, 0); 435 return isolate->heap()->ToBoolean(obj->IsJSArray()); 436 } 437 438 RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) { 439 HandleScope scope(isolate); 440 DCHECK(args.length() == 1); 441 CONVERT_ARG_HANDLE_CHECKED(Object, original_array, 0); 442 RETURN_RESULT_OR_FAILURE( 443 isolate, Object::ArraySpeciesConstructor(isolate, original_array)); 444 } 445 446 // ES7 22.1.3.11 Array.prototype.includes 447 RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) { 448 HandleScope shs(isolate); 449 DCHECK(args.length() == 3); 450 CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1); 451 CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2); 452 453 // Let O be ? ToObject(this value). 454 Handle<JSReceiver> object; 455 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 456 isolate, object, Object::ToObject(isolate, handle(args[0], isolate))); 457 458 // Let len be ? ToLength(? Get(O, "length")). 459 int64_t len; 460 { 461 if (object->map()->instance_type() == JS_ARRAY_TYPE) { 462 uint32_t len32 = 0; 463 bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32); 464 DCHECK(success); 465 USE(success); 466 len = len32; 467 } else { 468 Handle<Object> len_; 469 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 470 isolate, len_, 471 Object::GetProperty(object, isolate->factory()->length_string())); 472 473 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_, 474 Object::ToLength(isolate, len_)); 475 len = static_cast<int64_t>(len_->Number()); 476 DCHECK_EQ(len, len_->Number()); 477 } 478 } 479 480 if (len == 0) return isolate->heap()->false_value(); 481 482 // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step 483 // produces the value 0.) 484 int64_t start_from; 485 { 486 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index, 487 Object::ToInteger(isolate, from_index)); 488 double fp = from_index->Number(); 489 if (fp > len) return isolate->heap()->false_value(); 490 start_from = static_cast<int64_t>(fp); 491 } 492 493 int64_t index; 494 if (start_from >= 0) { 495 index = start_from; 496 } else { 497 index = len + start_from; 498 if (index < 0) { 499 index = 0; 500 } 501 } 502 503 // If the receiver is not a special receiver type, and the length is a valid 504 // element index, perform fast operation tailored to specific ElementsKinds. 505 if (object->map()->instance_type() > LAST_SPECIAL_RECEIVER_TYPE && 506 len < kMaxUInt32 && 507 JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) { 508 Handle<JSObject> obj = Handle<JSObject>::cast(object); 509 ElementsAccessor* elements = obj->GetElementsAccessor(); 510 Maybe<bool> result = elements->IncludesValue(isolate, obj, search_element, 511 static_cast<uint32_t>(index), 512 static_cast<uint32_t>(len)); 513 MAYBE_RETURN(result, isolate->heap()->exception()); 514 return *isolate->factory()->ToBoolean(result.FromJust()); 515 } 516 517 // Otherwise, perform slow lookups for special receiver types 518 for (; index < len; ++index) { 519 // Let elementK be the result of ? Get(O, ! ToString(k)). 520 Handle<Object> element_k; 521 { 522 Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index); 523 bool success; 524 LookupIterator it = LookupIterator::PropertyOrElement( 525 isolate, object, index_obj, &success); 526 DCHECK(success); 527 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k, 528 Object::GetProperty(&it)); 529 } 530 531 // If SameValueZero(searchElement, elementK) is true, return true. 532 if (search_element->SameValueZero(*element_k)) { 533 return isolate->heap()->true_value(); 534 } 535 } 536 return isolate->heap()->false_value(); 537 } 538 539 RUNTIME_FUNCTION(Runtime_ArrayIndexOf) { 540 HandleScope shs(isolate); 541 DCHECK(args.length() == 3); 542 CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1); 543 CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2); 544 545 // Let O be ? ToObject(this value). 546 Handle<Object> receiver_obj = args.at<Object>(0); 547 if (receiver_obj->IsNull(isolate) || receiver_obj->IsUndefined(isolate)) { 548 THROW_NEW_ERROR_RETURN_FAILURE( 549 isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined, 550 isolate->factory()->NewStringFromAsciiChecked( 551 "Array.prototype.indexOf"))); 552 } 553 Handle<JSReceiver> object; 554 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 555 isolate, object, Object::ToObject(isolate, args.at<Object>(0))); 556 557 // Let len be ? ToLength(? Get(O, "length")). 558 int64_t len; 559 { 560 if (object->IsJSArray()) { 561 uint32_t len32 = 0; 562 bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32); 563 DCHECK(success); 564 USE(success); 565 len = len32; 566 } else { 567 Handle<Object> len_; 568 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 569 isolate, len_, 570 Object::GetProperty(object, isolate->factory()->length_string())); 571 572 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_, 573 Object::ToLength(isolate, len_)); 574 len = static_cast<int64_t>(len_->Number()); 575 DCHECK_EQ(len, len_->Number()); 576 } 577 } 578 579 if (len == 0) return Smi::FromInt(-1); 580 581 // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step 582 // produces the value 0.) 583 int64_t start_from; 584 { 585 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index, 586 Object::ToInteger(isolate, from_index)); 587 double fp = from_index->Number(); 588 if (fp > len) return Smi::FromInt(-1); 589 start_from = static_cast<int64_t>(fp); 590 } 591 592 int64_t index; 593 if (start_from >= 0) { 594 index = start_from; 595 } else { 596 index = len + start_from; 597 if (index < 0) { 598 index = 0; 599 } 600 } 601 602 // If the receiver is not a special receiver type, and the length is a valid 603 // element index, perform fast operation tailored to specific ElementsKinds. 604 if (object->map()->instance_type() > LAST_SPECIAL_RECEIVER_TYPE && 605 len < kMaxUInt32 && 606 JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) { 607 Handle<JSObject> obj = Handle<JSObject>::cast(object); 608 ElementsAccessor* elements = obj->GetElementsAccessor(); 609 Maybe<int64_t> result = elements->IndexOfValue(isolate, obj, search_element, 610 static_cast<uint32_t>(index), 611 static_cast<uint32_t>(len)); 612 MAYBE_RETURN(result, isolate->heap()->exception()); 613 return *isolate->factory()->NewNumberFromInt64(result.FromJust()); 614 } 615 616 // Otherwise, perform slow lookups for special receiver types 617 for (; index < len; ++index) { 618 // Let elementK be the result of ? Get(O, ! ToString(k)). 619 Handle<Object> element_k; 620 { 621 Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index); 622 bool success; 623 LookupIterator it = LookupIterator::PropertyOrElement( 624 isolate, object, index_obj, &success); 625 DCHECK(success); 626 if (!JSReceiver::HasProperty(&it).FromJust()) { 627 continue; 628 } 629 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k, 630 Object::GetProperty(&it)); 631 if (search_element->StrictEquals(*element_k)) { 632 return *index_obj; 633 } 634 } 635 } 636 return Smi::FromInt(-1); 637 } 638 639 RUNTIME_FUNCTION(Runtime_SpreadIterablePrepare) { 640 HandleScope scope(isolate); 641 DCHECK_EQ(1, args.length()); 642 CONVERT_ARG_HANDLE_CHECKED(Object, spread, 0); 643 644 if (spread->IsJSArray()) { 645 // Check that the spread arg has fast elements 646 Handle<JSArray> spread_array = Handle<JSArray>::cast(spread); 647 ElementsKind array_kind = spread_array->GetElementsKind(); 648 649 // And that it has the orignal ArrayPrototype 650 JSObject* array_proto = JSObject::cast(spread_array->map()->prototype()); 651 Map* iterator_map = isolate->initial_array_iterator_prototype()->map(); 652 653 // Check that the iterator acts as expected. 654 // If IsArrayIteratorLookupChainIntact(), then we know that the initial 655 // ArrayIterator is being used. If the map of the prototype has changed, 656 // then take the slow path. 657 658 if (isolate->is_initial_array_prototype(array_proto) && 659 isolate->IsArrayIteratorLookupChainIntact() && 660 isolate->is_initial_array_iterator_prototype_map(iterator_map)) { 661 if (IsFastPackedElementsKind(array_kind)) { 662 return *spread; 663 } 664 if (IsFastHoleyElementsKind(array_kind) && 665 isolate->IsFastArrayConstructorPrototypeChainIntact()) { 666 return *spread; 667 } 668 } 669 } 670 671 Handle<JSFunction> spread_iterable_function = isolate->spread_iterable(); 672 673 Handle<Object> spreaded; 674 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 675 isolate, spreaded, 676 Execution::Call(isolate, spread_iterable_function, 677 isolate->factory()->undefined_value(), 1, &spread)); 678 679 return *spreaded; 680 } 681 682 } // namespace internal 683 } // namespace v8 684