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