diff options
Diffstat (limited to 'deps/v8/src/builtins/builtins-array.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-array.cc | 318 |
1 files changed, 170 insertions, 148 deletions
diff --git a/deps/v8/src/builtins/builtins-array.cc b/deps/v8/src/builtins/builtins-array.cc index ceeee5f37d..1e9de3dbe3 100644 --- a/deps/v8/src/builtins/builtins-array.cc +++ b/deps/v8/src/builtins/builtins-array.cc @@ -23,34 +23,6 @@ namespace internal { namespace { -inline bool ClampedToInteger(Isolate* isolate, Object* object, int* out) { - // This is an extended version of ECMA-262 7.1.11 handling signed values - // Try to convert object to a number and clamp values to [kMinInt, kMaxInt] - if (object->IsSmi()) { - *out = Smi::ToInt(object); - return true; - } else if (object->IsHeapNumber()) { - double value = HeapNumber::cast(object)->value(); - if (std::isnan(value)) { - *out = 0; - } else if (value > kMaxInt) { - *out = kMaxInt; - } else if (value < kMinInt) { - *out = kMinInt; - } else { - *out = static_cast<int>(value); - } - return true; - } else if (object->IsNullOrUndefined(isolate)) { - *out = 0; - return true; - } else if (object->IsBoolean()) { - *out = object->IsTrue(isolate); - return true; - } - return false; -} - inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate, JSArray* receiver) { return JSObject::PrototypeHasNoElements(isolate, receiver); @@ -79,36 +51,20 @@ inline bool HasOnlySimpleElements(Isolate* isolate, JSReceiver* receiver) { return true; } -// Returns |false| if not applicable. -// TODO(szuend): Refactor this function because it is getting hard to -// understand what each call-site actually checks. -V8_WARN_UNUSED_RESULT -inline bool EnsureJSArrayWithWritableFastElements(Isolate* isolate, - Handle<Object> receiver, - BuiltinArguments* args, - int first_arg_index, - int num_arguments) { - if (!receiver->IsJSArray()) return false; - Handle<JSArray> array = Handle<JSArray>::cast(receiver); - ElementsKind origin_kind = array->GetElementsKind(); - if (IsDictionaryElementsKind(origin_kind)) return false; - if (!array->map()->is_extensible()) return false; - if (args == nullptr) return true; - - // If there may be elements accessors in the prototype chain, the fast path - // cannot be used if there arguments to add to the array. - if (!IsJSArrayFastElementMovingAllowed(isolate, *array)) return false; +// This method may transition the elements kind of the JSArray once, to make +// sure that all elements provided as arguments in the specified range can be +// added without further elements kinds transitions. +void MatchArrayElementsKindToArguments(Isolate* isolate, Handle<JSArray> array, + BuiltinArguments* args, + int first_arg_index, int num_arguments) { + int args_length = args->length(); + if (first_arg_index >= args_length) return; - // Adding elements to the array prototype would break code that makes sure - // it has no elements. Handle that elsewhere. - if (isolate->IsAnyInitialArrayPrototype(array)) return false; + ElementsKind origin_kind = array->GetElementsKind(); - // Need to ensure that the arguments passed in args can be contained in - // the array. - int args_length = args->length(); - if (first_arg_index >= args_length) return true; + // We do not need to transition for PACKED/HOLEY_ELEMENTS. + if (IsObjectElementsKind(origin_kind)) return; - if (IsObjectElementsKind(origin_kind)) return true; ElementsKind target_kind = origin_kind; { DisallowHeapAllocation no_gc; @@ -131,20 +87,37 @@ inline bool EnsureJSArrayWithWritableFastElements(Isolate* isolate, HandleScope scope(isolate); JSObject::TransitionElementsKind(array, target_kind); } - return true; } -V8_WARN_UNUSED_RESULT static Object* CallJsIntrinsic( - Isolate* isolate, Handle<JSFunction> function, BuiltinArguments args) { - HandleScope handleScope(isolate); - int argc = args.length() - 1; - ScopedVector<Handle<Object>> argv(argc); - for (int i = 0; i < argc; ++i) { - argv[i] = args.at(i + 1); - } - RETURN_RESULT_OR_FAILURE( - isolate, - Execution::Call(isolate, function, args.receiver(), argc, argv.start())); +// Returns |false| if not applicable. +// TODO(szuend): Refactor this function because it is getting hard to +// understand what each call-site actually checks. +V8_WARN_UNUSED_RESULT +inline bool EnsureJSArrayWithWritableFastElements(Isolate* isolate, + Handle<Object> receiver, + BuiltinArguments* args, + int first_arg_index, + int num_arguments) { + if (!receiver->IsJSArray()) return false; + Handle<JSArray> array = Handle<JSArray>::cast(receiver); + ElementsKind origin_kind = array->GetElementsKind(); + if (IsDictionaryElementsKind(origin_kind)) return false; + if (!array->map()->is_extensible()) return false; + if (args == nullptr) return true; + + // If there may be elements accessors in the prototype chain, the fast path + // cannot be used if there arguments to add to the array. + if (!IsJSArrayFastElementMovingAllowed(isolate, *array)) return false; + + // Adding elements to the array prototype would break code that makes sure + // it has no elements. Handle that elsewhere. + if (isolate->IsAnyInitialArrayPrototype(array)) return false; + + // Need to ensure that the arguments passed in args can be contained in + // the array. + MatchArrayElementsKindToArguments(isolate, array, args, first_arg_index, + num_arguments); + return true; } // If |index| is Undefined, returns init_if_undefined. @@ -189,6 +162,24 @@ V8_WARN_UNUSED_RESULT Maybe<double> GetLengthProperty( return Just(raw_length_number->Number()); } +// Set "length" property, has "fast-path" for JSArrays. +// Returns Nothing if something went wrong. +V8_WARN_UNUSED_RESULT MaybeHandle<Object> SetLengthProperty( + Isolate* isolate, Handle<JSReceiver> receiver, double length) { + if (receiver->IsJSArray()) { + Handle<JSArray> array = Handle<JSArray>::cast(receiver); + if (!JSArray::HasReadOnlyLength(array)) { + DCHECK_LE(length, kMaxUInt32); + JSArray::SetLength(array, static_cast<uint32_t>(length)); + return receiver; + } + } + + return Object::SetProperty( + isolate, receiver, isolate->factory()->length_string(), + isolate->factory()->NewNumber(length), LanguageMode::kStrict); +} + V8_WARN_UNUSED_RESULT Object* GenericArrayFill(Isolate* isolate, Handle<JSReceiver> receiver, Handle<Object> value, @@ -350,7 +341,7 @@ V8_WARN_UNUSED_RESULT Object* GenericArrayPush(Isolate* isolate, // Must succeed since we always pass a valid key. DCHECK(success); MAYBE_RETURN(Object::SetProperty(&it, element, LanguageMode::kStrict, - Object::MAY_BE_STORE_FROM_KEYED), + StoreOrigin::kMaybeKeyed), ReadOnlyRoots(isolate).exception()); } @@ -485,110 +476,141 @@ BUILTIN(ArrayPop) { return *result; } -BUILTIN(ArrayShift) { - HandleScope scope(isolate); - Heap* heap = isolate->heap(); - Handle<Object> receiver = args.receiver(); +namespace { + +// Returns true, iff we can use ElementsAccessor for shifting. +V8_WARN_UNUSED_RESULT bool CanUseFastArrayShift(Isolate* isolate, + Handle<JSReceiver> receiver) { if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0, 0) || !IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) { - return CallJsIntrinsic(isolate, isolate->array_shift(), args); + return false; } + Handle<JSArray> array = Handle<JSArray>::cast(receiver); + return !JSArray::HasReadOnlyLength(array); +} - int len = Smi::ToInt(array->length()); - if (len == 0) return ReadOnlyRoots(heap).undefined_value(); +V8_WARN_UNUSED_RESULT Object* GenericArrayShift(Isolate* isolate, + Handle<JSReceiver> receiver, + double length) { + // 4. Let first be ? Get(O, "0"). + Handle<Object> first; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, first, + Object::GetElement(isolate, receiver, 0)); + + // 5. Let k be 1. + double k = 1; + + // 6. Repeat, while k < len. + while (k < length) { + // a. Let from be ! ToString(k). + Handle<String> from = + isolate->factory()->NumberToString(isolate->factory()->NewNumber(k)); + + // b. Let to be ! ToString(k-1). + Handle<String> to = isolate->factory()->NumberToString( + isolate->factory()->NewNumber(k - 1)); + + // c. Let fromPresent be ? HasProperty(O, from). + bool from_present; + MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, from_present, JSReceiver::HasProperty(receiver, from)); + + // d. If fromPresent is true, then. + if (from_present) { + // i. Let fromVal be ? Get(O, from). + Handle<Object> from_val; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, from_val, + Object::GetPropertyOrElement(isolate, receiver, from)); + + // ii. Perform ? Set(O, to, fromVal, true). + RETURN_FAILURE_ON_EXCEPTION( + isolate, Object::SetPropertyOrElement(isolate, receiver, to, from_val, + LanguageMode::kStrict)); + } else { // e. Else fromPresent is false, + // i. Perform ? DeletePropertyOrThrow(O, to). + MAYBE_RETURN(JSReceiver::DeletePropertyOrElement(receiver, to, + LanguageMode::kStrict), + ReadOnlyRoots(isolate).exception()); + } - if (JSArray::HasReadOnlyLength(array)) { - return CallJsIntrinsic(isolate, isolate->array_shift(), args); + // f. Increase k by 1. + ++k; } - Handle<Object> first = array->GetElementsAccessor()->Shift(array); + // 7. Perform ? DeletePropertyOrThrow(O, ! ToString(len-1)). + Handle<String> new_length = isolate->factory()->NumberToString( + isolate->factory()->NewNumber(length - 1)); + MAYBE_RETURN(JSReceiver::DeletePropertyOrElement(receiver, new_length, + LanguageMode::kStrict), + ReadOnlyRoots(isolate).exception()); + + // 8. Perform ? Set(O, "length", len-1, true). + RETURN_FAILURE_ON_EXCEPTION(isolate, + SetLengthProperty(isolate, receiver, length - 1)); + + // 9. Return first. return *first; } +} // namespace -BUILTIN(ArrayUnshift) { +BUILTIN(ArrayShift) { HandleScope scope(isolate); - Handle<Object> receiver = args.receiver(); - if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1, - args.length() - 1)) { - return CallJsIntrinsic(isolate, isolate->array_unshift(), args); - } - Handle<JSArray> array = Handle<JSArray>::cast(receiver); - int to_add = args.length() - 1; - if (to_add == 0) return array->length(); - // Currently fixed arrays cannot grow too big, so we should never hit this. - DCHECK_LE(to_add, Smi::kMaxValue - Smi::ToInt(array->length())); + // 1. Let O be ? ToObject(this value). + Handle<JSReceiver> receiver; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, receiver, Object::ToObject(isolate, args.receiver())); - if (JSArray::HasReadOnlyLength(array)) { - return CallJsIntrinsic(isolate, isolate->array_unshift(), args); + // 2. Let len be ? ToLength(? Get(O, "length")). + double length; + MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, length, GetLengthProperty(isolate, receiver)); + + // 3. If len is zero, then. + if (length == 0) { + // a. Perform ? Set(O, "length", 0, true). + RETURN_FAILURE_ON_EXCEPTION(isolate, + SetLengthProperty(isolate, receiver, length)); + + // b. Return undefined. + return ReadOnlyRoots(isolate).undefined_value(); } - ElementsAccessor* accessor = array->GetElementsAccessor(); - int new_length = accessor->Unshift(array, &args, to_add); - return Smi::FromInt(new_length); + if (CanUseFastArrayShift(isolate, receiver)) { + Handle<JSArray> array = Handle<JSArray>::cast(receiver); + return *array->GetElementsAccessor()->Shift(array); + } + + return GenericArrayShift(isolate, receiver, length); } -BUILTIN(ArraySplice) { +BUILTIN(ArrayUnshift) { HandleScope scope(isolate); - Handle<Object> receiver = args.receiver(); - if (V8_UNLIKELY( - !EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3, - args.length() - 3) || - // If this is a subclass of Array, then call out to JS. - !Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) || - // If anything with @@species has been messed with, call out to JS. - !isolate->IsArraySpeciesLookupChainIntact())) { - return CallJsIntrinsic(isolate, isolate->array_splice(), args); - } - Handle<JSArray> array = Handle<JSArray>::cast(receiver); + DCHECK(args.receiver()->IsJSArray()); + Handle<JSArray> array = Handle<JSArray>::cast(args.receiver()); - int argument_count = args.length() - 1; - int relative_start = 0; - if (argument_count > 0) { - DisallowHeapAllocation no_gc; - if (!ClampedToInteger(isolate, args[1], &relative_start)) { - AllowHeapAllocation allow_allocation; - return CallJsIntrinsic(isolate, isolate->array_splice(), args); - } - } - int len = Smi::ToInt(array->length()); - // clip relative start to [0, len] - int actual_start = (relative_start < 0) ? Max(len + relative_start, 0) - : Min(relative_start, len); - - int actual_delete_count; - if (argument_count == 1) { - // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is - // given as a request to delete all the elements from the start. - // And it differs from the case of undefined delete count. - // This does not follow ECMA-262, but we do the same for compatibility. - DCHECK_GE(len - actual_start, 0); - actual_delete_count = len - actual_start; - } else { - int delete_count = 0; - DisallowHeapAllocation no_gc; - if (argument_count > 1) { - if (!ClampedToInteger(isolate, args[2], &delete_count)) { - AllowHeapAllocation allow_allocation; - return CallJsIntrinsic(isolate, isolate->array_splice(), args); - } - } - actual_delete_count = Min(Max(delete_count, 0), len - actual_start); - } + // These are checked in the Torque builtin. + DCHECK(array->map()->is_extensible()); + DCHECK(!IsDictionaryElementsKind(array->GetElementsKind())); + DCHECK(IsJSArrayFastElementMovingAllowed(isolate, *array)); + DCHECK(!isolate->IsAnyInitialArrayPrototype(array)); - int add_count = (argument_count > 1) ? (argument_count - 2) : 0; - int new_length = len - actual_delete_count + add_count; + MatchArrayElementsKindToArguments(isolate, array, &args, 1, + args.length() - 1); + + int to_add = args.length() - 1; + if (to_add == 0) return array->length(); + + // Currently fixed arrays cannot grow too big, so we should never hit this. + DCHECK_LE(to_add, Smi::kMaxValue - Smi::ToInt(array->length())); + DCHECK(!JSArray::HasReadOnlyLength(array)); - if (new_length != len && JSArray::HasReadOnlyLength(array)) { - AllowHeapAllocation allow_allocation; - return CallJsIntrinsic(isolate, isolate->array_splice(), args); - } ElementsAccessor* accessor = array->GetElementsAccessor(); - Handle<JSArray> result_array = accessor->Splice( - array, actual_start, actual_delete_count, &args, add_count); - return *result_array; + int new_length = accessor->Unshift(array, &args, to_add); + return Smi::FromInt(new_length); } // Array Concat ------------------------------------------------------------- |