diff options
Diffstat (limited to 'deps/v8/src/builtins/builtins-array.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-array.cc | 198 |
1 files changed, 183 insertions, 15 deletions
diff --git a/deps/v8/src/builtins/builtins-array.cc b/deps/v8/src/builtins/builtins-array.cc index 5154b904f5f..ceeee5f37db 100644 --- a/deps/v8/src/builtins/builtins-array.cc +++ b/deps/v8/src/builtins/builtins-array.cc @@ -2,19 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "src/builtins/builtins-utils-inl.h" #include "src/builtins/builtins.h" -#include "src/builtins/builtins-utils.h" - #include "src/code-factory.h" #include "src/code-stub-assembler.h" #include "src/contexts.h" #include "src/counters.h" -#include "src/elements.h" +#include "src/debug/debug.h" +#include "src/elements-inl.h" #include "src/global-handles.h" #include "src/isolate.h" #include "src/lookup.h" #include "src/objects-inl.h" #include "src/objects/hash-table-inl.h" +#include "src/objects/js-array-inl.h" #include "src/prototype.h" namespace v8 { @@ -79,11 +80,14 @@ inline bool HasOnlySimpleElements(Isolate* isolate, JSReceiver* receiver) { } // 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_added_arg) { + int first_arg_index, + int num_arguments) { if (!receiver->IsJSArray()) return false; Handle<JSArray> array = Handle<JSArray>::cast(receiver); ElementsKind origin_kind = array->GetElementsKind(); @@ -102,13 +106,14 @@ inline bool EnsureJSArrayWithWritableFastElements(Isolate* isolate, // Need to ensure that the arguments passed in args can be contained in // the array. int args_length = args->length(); - if (first_added_arg >= args_length) return true; + if (first_arg_index >= args_length) return true; if (IsObjectElementsKind(origin_kind)) return true; ElementsKind target_kind = origin_kind; { DisallowHeapAllocation no_gc; - for (int i = first_added_arg; i < args_length; i++) { + int last_arg_index = std::min(first_arg_index + num_arguments, args_length); + for (int i = first_arg_index; i < last_arg_index; i++) { Object* arg = (*args)[i]; if (arg->IsHeapObject()) { if (arg->IsHeapNumber()) { @@ -142,6 +147,164 @@ V8_WARN_UNUSED_RESULT static Object* CallJsIntrinsic( Execution::Call(isolate, function, args.receiver(), argc, argv.start())); } +// If |index| is Undefined, returns init_if_undefined. +// If |index| is negative, returns length + index. +// If |index| is positive, returns index. +// Returned value is guaranteed to be in the interval of [0, length]. +V8_WARN_UNUSED_RESULT Maybe<double> GetRelativeIndex(Isolate* isolate, + double length, + Handle<Object> index, + double init_if_undefined) { + double relative_index = init_if_undefined; + if (!index->IsUndefined()) { + Handle<Object> relative_index_obj; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, relative_index_obj, + Object::ToInteger(isolate, index), + Nothing<double>()); + relative_index = relative_index_obj->Number(); + } + + if (relative_index < 0) { + return Just(std::max(length + relative_index, 0.0)); + } + + return Just(std::min(relative_index, length)); +} + +// Returns "length", has "fast-path" for JSArrays. +V8_WARN_UNUSED_RESULT Maybe<double> GetLengthProperty( + Isolate* isolate, Handle<JSReceiver> receiver) { + if (receiver->IsJSArray()) { + Handle<JSArray> array = Handle<JSArray>::cast(receiver); + double length = array->length()->Number(); + DCHECK(0 <= length && length <= kMaxSafeInteger); + + return Just(length); + } + + Handle<Object> raw_length_number; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, raw_length_number, + Object::GetLengthFromArrayLike(isolate, receiver), Nothing<double>()); + return Just(raw_length_number->Number()); +} + +V8_WARN_UNUSED_RESULT Object* GenericArrayFill(Isolate* isolate, + Handle<JSReceiver> receiver, + Handle<Object> value, + double start, double end) { + // 7. Repeat, while k < final. + while (start < end) { + // a. Let Pk be ! ToString(k). + Handle<String> index = isolate->factory()->NumberToString( + isolate->factory()->NewNumber(start)); + + // b. Perform ? Set(O, Pk, value, true). + RETURN_FAILURE_ON_EXCEPTION( + isolate, Object::SetPropertyOrElement(isolate, receiver, index, value, + LanguageMode::kStrict)); + + // c. Increase k by 1. + ++start; + } + + // 8. Return O. + return *receiver; +} + +V8_WARN_UNUSED_RESULT bool TryFastArrayFill( + Isolate* isolate, BuiltinArguments* args, Handle<JSReceiver> receiver, + Handle<Object> value, double start_index, double end_index) { + // If indices are too large, use generic path since they are stored as + // properties, not in the element backing store. + if (end_index > kMaxUInt32) return false; + if (!receiver->IsJSObject()) return false; + + if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, args, 1, 1)) { + return false; + } + + Handle<JSArray> array = Handle<JSArray>::cast(receiver); + + // If no argument was provided, we fill the array with 'undefined'. + // EnsureJSArrayWith... does not handle that case so we do it here. + // TODO(szuend): Pass target elements kind to EnsureJSArrayWith... when + // it gets refactored. + if (args->length() == 1 && array->GetElementsKind() != PACKED_ELEMENTS) { + // Use a short-lived HandleScope to avoid creating several copies of the + // elements handle which would cause issues when left-trimming later-on. + HandleScope scope(isolate); + JSObject::TransitionElementsKind(array, PACKED_ELEMENTS); + } + + DCHECK_LE(start_index, kMaxUInt32); + DCHECK_LE(end_index, kMaxUInt32); + + uint32_t start, end; + CHECK(DoubleToUint32IfEqualToSelf(start_index, &start)); + CHECK(DoubleToUint32IfEqualToSelf(end_index, &end)); + + ElementsAccessor* accessor = array->GetElementsAccessor(); + accessor->Fill(array, value, start, end); + return true; +} +} // namespace + +BUILTIN(ArrayPrototypeFill) { + HandleScope scope(isolate); + + if (isolate->debug_execution_mode() == DebugInfo::kSideEffects) { + if (!isolate->debug()->PerformSideEffectCheckForObject(args.receiver())) { + return ReadOnlyRoots(isolate).exception(); + } + } + + // 1. Let O be ? ToObject(this value). + Handle<JSReceiver> receiver; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, receiver, Object::ToObject(isolate, args.receiver())); + + // 2. Let len be ? ToLength(? Get(O, "length")). + double length; + MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, length, GetLengthProperty(isolate, receiver)); + + // 3. Let relativeStart be ? ToInteger(start). + // 4. If relativeStart < 0, let k be max((len + relativeStart), 0); + // else let k be min(relativeStart, len). + Handle<Object> start = args.atOrUndefined(isolate, 2); + + double start_index; + MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, start_index, GetRelativeIndex(isolate, length, start, 0)); + + // 5. If end is undefined, let relativeEnd be len; + // else let relativeEnd be ? ToInteger(end). + // 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0); + // else let final be min(relativeEnd, len). + Handle<Object> end = args.atOrUndefined(isolate, 3); + + double end_index; + MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, end_index, GetRelativeIndex(isolate, length, end, length)); + + if (start_index >= end_index) return *receiver; + + // Ensure indexes are within array bounds + DCHECK_LE(0, start_index); + DCHECK_LE(start_index, end_index); + DCHECK_LE(end_index, length); + + Handle<Object> value = args.atOrUndefined(isolate, 1); + + if (TryFastArrayFill(isolate, &args, receiver, value, start_index, + end_index)) { + return *receiver; + } + return GenericArrayFill(isolate, receiver, value, start_index, end_index); +} + +namespace { V8_WARN_UNUSED_RESULT Object* GenericArrayPush(Isolate* isolate, BuiltinArguments* args) { // 1. Let O be ? ToObject(this value). @@ -153,7 +316,7 @@ V8_WARN_UNUSED_RESULT Object* GenericArrayPush(Isolate* isolate, Handle<Object> raw_length_number; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( isolate, raw_length_number, - Object::GetLengthFromArrayLike(isolate, Handle<Object>::cast(receiver))); + Object::GetLengthFromArrayLike(isolate, receiver)); // 3. Let args be a List whose elements are, in left to right order, // the arguments that were passed to this function invocation. @@ -210,7 +373,8 @@ V8_WARN_UNUSED_RESULT Object* GenericArrayPush(Isolate* isolate, BUILTIN(ArrayPush) { HandleScope scope(isolate); Handle<Object> receiver = args.receiver(); - if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) { + if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1, + args.length() - 1)) { return GenericArrayPush(isolate, &args); } @@ -293,7 +457,8 @@ V8_WARN_UNUSED_RESULT Object* GenericArrayPop(Isolate* isolate, BUILTIN(ArrayPop) { HandleScope scope(isolate); Handle<Object> receiver = args.receiver(); - if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0)) { + if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0, + 0)) { return GenericArrayPop(isolate, &args); } Handle<JSArray> array = Handle<JSArray>::cast(receiver); @@ -324,7 +489,8 @@ BUILTIN(ArrayShift) { HandleScope scope(isolate); Heap* heap = isolate->heap(); Handle<Object> receiver = args.receiver(); - if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0) || + if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0, + 0) || !IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) { return CallJsIntrinsic(isolate, isolate->array_shift(), args); } @@ -344,7 +510,8 @@ BUILTIN(ArrayShift) { BUILTIN(ArrayUnshift) { HandleScope scope(isolate); Handle<Object> receiver = args.receiver(); - if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) { + if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1, + args.length() - 1)) { return CallJsIntrinsic(isolate, isolate->array_unshift(), args); } Handle<JSArray> array = Handle<JSArray>::cast(receiver); @@ -367,7 +534,8 @@ BUILTIN(ArraySplice) { HandleScope scope(isolate); Handle<Object> receiver = args.receiver(); if (V8_UNLIKELY( - !EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3) || + !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. @@ -662,7 +830,7 @@ uint32_t EstimateElementCount(Isolate* isolate, Handle<JSArray> array) { } break; } -#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS: +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS: TYPED_ARRAYS(TYPED_ARRAY_CASE) #undef TYPED_ARRAY_CASE @@ -733,7 +901,7 @@ void CollectElementIndices(Isolate* isolate, Handle<JSObject> object, }); break; } -#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS: +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS: TYPED_ARRAYS(TYPED_ARRAY_CASE) #undef TYPED_ARRAY_CASE @@ -958,7 +1126,7 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver, } case NO_ELEMENTS: break; -#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS: +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS: TYPED_ARRAYS(TYPED_ARRAY_CASE) #undef TYPED_ARRAY_CASE return IterateElementsSlow(isolate, receiver, length, visitor); |