[array] Move Array.p.push fall-back from JS to C++
This CL extends the existing ArrayPush C++ builtin with a generic slow-path that replaces the JavaScript fall-back. Bug: v8:7624 Change-Id: I1e8431601e8a872f3c5afba5d486f37fd5781d60 Reviewed-on: https://chromium-review.googlesource.com/1126922 Reviewed-by: Camillo Bruni <cbruni@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Simon Zünd <szuend@google.com> Cr-Commit-Position: refs/heads/master@{#54282}
This commit is contained in:
parent
5f9686616c
commit
d447525297
@ -141,30 +141,95 @@ V8_WARN_UNUSED_RESULT static Object* CallJsIntrinsic(
|
|||||||
isolate,
|
isolate,
|
||||||
Execution::Call(isolate, function, args.receiver(), argc, argv.start()));
|
Execution::Call(isolate, function, args.receiver(), argc, argv.start()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
V8_WARN_UNUSED_RESULT Object* GenericArrayPush(Isolate* isolate,
|
||||||
|
BuiltinArguments* args) {
|
||||||
|
// 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")).
|
||||||
|
Handle<Object> raw_length_number;
|
||||||
|
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||||
|
isolate, raw_length_number,
|
||||||
|
Object::GetLengthFromArrayLike(isolate, Handle<Object>::cast(receiver)));
|
||||||
|
|
||||||
|
// 3. Let args be a List whose elements are, in left to right order,
|
||||||
|
// the arguments that were passed to this function invocation.
|
||||||
|
// 4. Let arg_count be the number of elements in args.
|
||||||
|
int arg_count = args->length() - 1;
|
||||||
|
|
||||||
|
// 5. If len + arg_count > 2^53-1, throw a TypeError exception.
|
||||||
|
double length = raw_length_number->Number();
|
||||||
|
if (arg_count > kMaxSafeInteger - length) {
|
||||||
|
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||||
|
isolate, NewTypeError(MessageTemplate::kPushPastSafeLength,
|
||||||
|
isolate->factory()->NewNumberFromInt(arg_count),
|
||||||
|
raw_length_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Repeat, while args is not empty.
|
||||||
|
for (int i = 0; i < arg_count; ++i) {
|
||||||
|
// a. Remove the first element from args and let E be the value of the
|
||||||
|
// element.
|
||||||
|
Handle<Object> element = args->at(i + 1);
|
||||||
|
|
||||||
|
// b. Perform ? Set(O, ! ToString(len), E, true).
|
||||||
|
if (length <= static_cast<double>(JSArray::kMaxArrayIndex)) {
|
||||||
|
RETURN_FAILURE_ON_EXCEPTION(
|
||||||
|
isolate, Object::SetElement(isolate, receiver, length, element,
|
||||||
|
LanguageMode::kStrict));
|
||||||
|
} else {
|
||||||
|
bool success;
|
||||||
|
LookupIterator it = LookupIterator::PropertyOrElement(
|
||||||
|
isolate, receiver, isolate->factory()->NewNumber(length), &success);
|
||||||
|
// 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),
|
||||||
|
ReadOnlyRoots(isolate).exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
// c. Let len be len+1.
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Perform ? Set(O, "length", len, true).
|
||||||
|
Handle<Object> final_length = isolate->factory()->NewNumber(length);
|
||||||
|
RETURN_FAILURE_ON_EXCEPTION(
|
||||||
|
isolate,
|
||||||
|
Object::SetProperty(receiver, isolate->factory()->length_string(),
|
||||||
|
final_length, LanguageMode::kStrict));
|
||||||
|
|
||||||
|
// 8. Return len.
|
||||||
|
return *final_length;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
BUILTIN(ArrayPush) {
|
BUILTIN(ArrayPush) {
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
Handle<Object> receiver = args.receiver();
|
Handle<Object> receiver = args.receiver();
|
||||||
if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
|
if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
|
||||||
return CallJsIntrinsic(isolate, isolate->array_push(), args);
|
return GenericArrayPush(isolate, &args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast Elements Path
|
// Fast Elements Path
|
||||||
int to_add = args.length() - 1;
|
int to_add = args.length() - 1;
|
||||||
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
|
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
|
||||||
int len = Smi::ToInt(array->length());
|
uint32_t len = static_cast<uint32_t>(array->length()->Number());
|
||||||
if (to_add == 0) return Smi::FromInt(len);
|
if (to_add == 0) return *isolate->factory()->NewNumberFromUint(len);
|
||||||
|
|
||||||
// Currently fixed arrays cannot grow too big, so we should never hit this.
|
// Currently fixed arrays cannot grow too big, so we should never hit this.
|
||||||
DCHECK_LE(to_add, Smi::kMaxValue - Smi::ToInt(array->length()));
|
DCHECK_LE(to_add, Smi::kMaxValue - Smi::ToInt(array->length()));
|
||||||
|
|
||||||
if (JSArray::HasReadOnlyLength(array)) {
|
if (JSArray::HasReadOnlyLength(array)) {
|
||||||
return CallJsIntrinsic(isolate, isolate->array_push(), args);
|
return GenericArrayPush(isolate, &args);
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementsAccessor* accessor = array->GetElementsAccessor();
|
ElementsAccessor* accessor = array->GetElementsAccessor();
|
||||||
int new_length = accessor->Push(array, &args, to_add);
|
uint32_t new_length = accessor->Push(array, &args, to_add);
|
||||||
return Smi::FromInt(new_length);
|
return *isolate->factory()->NewNumberFromUint((new_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
BUILTIN(ArrayPop) {
|
BUILTIN(ArrayPop) {
|
||||||
|
@ -72,7 +72,6 @@ enum ContextLookupFlags {
|
|||||||
|
|
||||||
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
|
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
|
||||||
V(ARRAY_POP_INDEX, JSFunction, array_pop) \
|
V(ARRAY_POP_INDEX, JSFunction, array_pop) \
|
||||||
V(ARRAY_PUSH_INDEX, JSFunction, array_push) \
|
|
||||||
V(ARRAY_SHIFT_INDEX, JSFunction, array_shift) \
|
V(ARRAY_SHIFT_INDEX, JSFunction, array_shift) \
|
||||||
V(ARRAY_SPLICE_INDEX, JSFunction, array_splice) \
|
V(ARRAY_SPLICE_INDEX, JSFunction, array_splice) \
|
||||||
V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift) \
|
V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift) \
|
||||||
|
@ -409,28 +409,6 @@ function ArrayPopFallback() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Appends the arguments to the end of the array and returns the new
|
|
||||||
// length of the array. See ECMA-262, section 15.4.4.7.
|
|
||||||
function ArrayPushFallback() {
|
|
||||||
var array = TO_OBJECT(this);
|
|
||||||
var n = TO_LENGTH(array.length);
|
|
||||||
var m = arguments.length;
|
|
||||||
|
|
||||||
// Subtract n from kMaxSafeInteger rather than testing m + n >
|
|
||||||
// kMaxSafeInteger. n may already be kMaxSafeInteger. In that case adding
|
|
||||||
// e.g., 1 would not be safe.
|
|
||||||
if (m > kMaxSafeInteger - n) throw %make_type_error(kPushPastSafeLength, m, n);
|
|
||||||
|
|
||||||
for (var i = 0; i < m; i++) {
|
|
||||||
array[i+n] = arguments[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
var new_length = n + m;
|
|
||||||
array.length = new_length;
|
|
||||||
return new_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// For implementing reverse() on large, sparse arrays.
|
// For implementing reverse() on large, sparse arrays.
|
||||||
function SparseReverse(array, len) {
|
function SparseReverse(array, len) {
|
||||||
var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
|
var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
|
||||||
@ -1060,7 +1038,6 @@ utils.Export(function(to) {
|
|||||||
"array_values_iterator", ArrayValues,
|
"array_values_iterator", ArrayValues,
|
||||||
// Fallback implementations of Array builtins.
|
// Fallback implementations of Array builtins.
|
||||||
"array_pop", ArrayPopFallback,
|
"array_pop", ArrayPopFallback,
|
||||||
"array_push", ArrayPushFallback,
|
|
||||||
"array_shift", ArrayShiftFallback,
|
"array_shift", ArrayShiftFallback,
|
||||||
"array_splice", ArraySpliceFallback,
|
"array_splice", ArraySpliceFallback,
|
||||||
"array_unshift", ArrayUnshiftFallback,
|
"array_unshift", ArrayUnshiftFallback,
|
||||||
|
@ -94,6 +94,9 @@ class JSArray : public JSObject {
|
|||||||
AllocationMemento::kSize) >>
|
AllocationMemento::kSize) >>
|
||||||
kDoubleSizeLog2;
|
kDoubleSizeLog2;
|
||||||
|
|
||||||
|
// Valid array indices range from +0 <= i < 2^32 - 1 (kMaxUInt32).
|
||||||
|
static const uint32_t kMaxArrayIndex = kMaxUInt32 - 1;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(JSArray);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(JSArray);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user