Commit 0f1c626d authored by caitp's avatar caitp Committed by Commit bot

[typedarrays] move %TypedArray%.prototype.copyWithin to C++

- Removes shared InnerArrayCopyWithin JS builtin from src/js/array.js
- Implements %TypedArray%.prototype.copyWithin as a C++ builtin, which
relies on std::memmove rather than accessing individual eleements.
- Fixes the case where copyWithin is invoked on a TypedArray with a
detached buffer.
- Add tests to ensure that +/-Infinity (for all 3 parameters) is handled correctly by the
algorithm

The C++ version gets through the benchmark more than 25000 times as
quickly as the JS implementation.

BUG=v8:5925, v8:5929, v8:4648
R=cbruni@chromium.org, adamk@chromium.org, littledan@chromium.org

Review-Url: https://codereview.chromium.org/2671233002
Cr-Commit-Position: refs/heads/master@{#42975}
parent 9e248dde
...@@ -2483,6 +2483,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -2483,6 +2483,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
values->shared()->set_builtin_function_id(kTypedArrayValues); values->shared()->set_builtin_function_id(kTypedArrayValues);
JSObject::AddProperty(prototype, factory->iterator_symbol(), values, JSObject::AddProperty(prototype, factory->iterator_symbol(), values,
DONT_ENUM); DONT_ENUM);
// TODO(caitp): alphasort accessors/methods
SimpleInstallFunction(prototype, "copyWithin",
Builtins::kTypedArrayPrototypeCopyWithin, 2, false);
} }
{ // -- T y p e d A r r a y s { // -- T y p e d A r r a y s
......
...@@ -167,5 +167,95 @@ void Builtins::Generate_TypedArrayPrototypeKeys( ...@@ -167,5 +167,95 @@ void Builtins::Generate_TypedArrayPrototypeKeys(
state, "%TypedArray%.prototype.keys()"); state, "%TypedArray%.prototype.keys()");
} }
namespace {
MaybeHandle<JSTypedArray> ValiadateTypedArray(Isolate* isolate,
Handle<Object> receiver,
const char* method_name) {
if (V8_UNLIKELY(!receiver->IsJSTypedArray())) {
const MessageTemplate::Template message = MessageTemplate::kNotTypedArray;
THROW_NEW_ERROR(isolate, NewTypeError(message), JSTypedArray);
}
Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(receiver);
if (V8_UNLIKELY(array->WasNeutered())) {
const MessageTemplate::Template message = MessageTemplate::kNotTypedArray;
Handle<String> operation =
isolate->factory()->NewStringFromAsciiChecked(method_name);
THROW_NEW_ERROR(isolate, NewTypeError(message, operation), JSTypedArray);
}
return array;
}
int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
int64_t relative;
if (V8_LIKELY(num->IsSmi())) {
relative = Smi::cast(*num)->value();
} else {
DCHECK(num->IsHeapNumber());
double fp = HeapNumber::cast(*num)->value();
if (V8_UNLIKELY(!std::isfinite(fp))) {
// +Infinity / -Infinity
DCHECK(!std::isnan(fp));
return fp < 0 ? minimum : maximum;
}
relative = static_cast<int64_t>(fp);
}
return relative < 0 ? std::max<int64_t>(relative + maximum, minimum)
: std::min<int64_t>(relative, maximum);
}
} // namespace
BUILTIN(TypedArrayPrototypeCopyWithin) {
HandleScope scope(isolate);
Handle<JSTypedArray> array;
const char* method = "%TypedArray%.prototype.copyWithin";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array, ValiadateTypedArray(isolate, args.receiver(), method));
int64_t len = array->length_value();
int64_t to = 0;
int64_t from = 0;
int64_t final = len;
if (V8_LIKELY(args.length() > 1)) {
Handle<Object> num;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, num, Object::ToInteger(isolate, args.at<Object>(1)));
to = CapRelativeIndex(num, 0, len);
if (args.length() > 2) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
from = CapRelativeIndex(num, 0, len);
Handle<Object> end = args.atOrUndefined(isolate, 3);
if (!end->IsUndefined(isolate)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
Object::ToInteger(isolate, end));
final = CapRelativeIndex(num, 0, len);
}
}
}
int64_t count = std::min<int64_t>(final - from, len - to);
if (count <= 0) return *array;
Handle<FixedTypedArrayBase> elements(
FixedTypedArrayBase::cast(array->elements()));
size_t element_size = array->element_size();
to = to * element_size;
from = from * element_size;
count = count * element_size;
uint8_t* data = static_cast<uint8_t*>(elements->DataPtr());
std::memmove(data + to, data + from, count);
return *array;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -800,7 +800,9 @@ class Isolate; ...@@ -800,7 +800,9 @@ class Isolate;
/* ES6 #sec-%typedarray%.prototype.keys */ \ /* ES6 #sec-%typedarray%.prototype.keys */ \
TFJ(TypedArrayPrototypeKeys, 0) \ TFJ(TypedArrayPrototypeKeys, 0) \
/* ES6 #sec-%typedarray%.prototype.values */ \ /* ES6 #sec-%typedarray%.prototype.values */ \
TFJ(TypedArrayPrototypeValues, 0) TFJ(TypedArrayPrototypeValues, 0) \
/* ES6 #sec-%typedarray%.prototype.copywithin */ \
CPP(TypedArrayPrototypeCopyWithin)
#define IGNORE_BUILTIN(...) #define IGNORE_BUILTIN(...)
......
...@@ -1270,7 +1270,13 @@ function ArrayReduceRight(callback, current) { ...@@ -1270,7 +1270,13 @@ function ArrayReduceRight(callback, current) {
} }
function InnerArrayCopyWithin(target, start, end, array, length) { // ES6 draft 03-17-15, section 22.1.3.3
function ArrayCopyWithin(target, start, end) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
var array = TO_OBJECT(this);
var length = TO_LENGTH(array.length);
target = TO_INTEGER(target); target = TO_INTEGER(target);
var to; var to;
if (target < 0) { if (target < 0) {
...@@ -1318,17 +1324,6 @@ function InnerArrayCopyWithin(target, start, end, array, length) { ...@@ -1318,17 +1324,6 @@ function InnerArrayCopyWithin(target, start, end, array, length) {
} }
// ES6 draft 03-17-15, section 22.1.3.3
function ArrayCopyWithin(target, start, end) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
var array = TO_OBJECT(this);
var length = TO_LENGTH(array.length);
return InnerArrayCopyWithin(target, start, end, array, length);
}
function InnerArrayFind(predicate, thisArg, array, length) { function InnerArrayFind(predicate, thisArg, array, length) {
if (!IS_CALLABLE(predicate)) { if (!IS_CALLABLE(predicate)) {
throw %make_type_error(kCalledNonCallable, predicate); throw %make_type_error(kCalledNonCallable, predicate);
...@@ -1615,7 +1610,6 @@ utils.Export(function(to) { ...@@ -1615,7 +1610,6 @@ utils.Export(function(to) {
to.ArrayPush = ArrayPush; to.ArrayPush = ArrayPush;
to.ArrayToString = ArrayToString; to.ArrayToString = ArrayToString;
to.ArrayValues = ArrayValues; to.ArrayValues = ArrayValues;
to.InnerArrayCopyWithin = InnerArrayCopyWithin;
to.InnerArrayEvery = InnerArrayEvery; to.InnerArrayEvery = InnerArrayEvery;
to.InnerArrayFill = InnerArrayFill; to.InnerArrayFill = InnerArrayFill;
to.InnerArrayFilter = InnerArrayFilter; to.InnerArrayFilter = InnerArrayFilter;
......
...@@ -20,7 +20,6 @@ var GlobalArray = global.Array; ...@@ -20,7 +20,6 @@ var GlobalArray = global.Array;
var GlobalArrayBuffer = global.ArrayBuffer; var GlobalArrayBuffer = global.ArrayBuffer;
var GlobalArrayBufferPrototype = GlobalArrayBuffer.prototype; var GlobalArrayBufferPrototype = GlobalArrayBuffer.prototype;
var GlobalObject = global.Object; var GlobalObject = global.Object;
var InnerArrayCopyWithin;
var InnerArrayEvery; var InnerArrayEvery;
var InnerArrayFill; var InnerArrayFill;
var InnerArrayFilter; var InnerArrayFilter;
...@@ -68,7 +67,6 @@ utils.Import(function(from) { ...@@ -68,7 +67,6 @@ utils.Import(function(from) {
ArrayValues = from.ArrayValues; ArrayValues = from.ArrayValues;
GetIterator = from.GetIterator; GetIterator = from.GetIterator;
GetMethod = from.GetMethod; GetMethod = from.GetMethod;
InnerArrayCopyWithin = from.InnerArrayCopyWithin;
InnerArrayEvery = from.InnerArrayEvery; InnerArrayEvery = from.InnerArrayEvery;
InnerArrayFill = from.InnerArrayFill; InnerArrayFill = from.InnerArrayFill;
InnerArrayFilter = from.InnerArrayFilter; InnerArrayFilter = from.InnerArrayFilter;
...@@ -438,17 +436,6 @@ function TypedArrayGetToStringTag() { ...@@ -438,17 +436,6 @@ function TypedArrayGetToStringTag() {
} }
function TypedArrayCopyWithin(target, start, end) {
if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
var length = %_TypedArrayGetLength(this);
// TODO(littledan): Replace with a memcpy for better performance
return InnerArrayCopyWithin(target, start, end, this, length);
}
%FunctionSetLength(TypedArrayCopyWithin, 2);
// ES6 draft 05-05-15, section 22.2.3.7 // ES6 draft 05-05-15, section 22.2.3.7
function TypedArrayEvery(f, receiver) { function TypedArrayEvery(f, receiver) {
if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray); if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
...@@ -858,7 +845,6 @@ utils.InstallGetter(GlobalTypedArray.prototype, toStringTagSymbol, ...@@ -858,7 +845,6 @@ utils.InstallGetter(GlobalTypedArray.prototype, toStringTagSymbol,
utils.InstallFunctions(GlobalTypedArray.prototype, DONT_ENUM, [ utils.InstallFunctions(GlobalTypedArray.prototype, DONT_ENUM, [
"subarray", TypedArraySubArray, "subarray", TypedArraySubArray,
"set", TypedArraySet, "set", TypedArraySet,
"copyWithin", TypedArrayCopyWithin,
"every", TypedArrayEvery, "every", TypedArrayEvery,
"fill", TypedArrayFill, "fill", TypedArrayFill,
"filter", TypedArrayFilter, "filter", TypedArrayFilter,
......
...@@ -171,3 +171,57 @@ CheckEachTypedArray(function copyWithinNullEnd(constructor) { ...@@ -171,3 +171,57 @@ CheckEachTypedArray(function copyWithinNullEnd(constructor) {
assertArrayEquals([1, 2, 3, 4, 5], assertArrayEquals([1, 2, 3, 4, 5],
new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3, null)); new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3, null));
}); });
CheckEachTypedArray(function copyWithinMinusInfinityTarget(constructor) {
var arr = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var expected = [6, 7, 8, 9, 10, 6, 7, 8, 9, 10];
assertArrayEquals(expected, arr.copyWithin(-Infinity, 5));
assertEquals(10, arr.length);
});
CheckEachTypedArray(function copyWithinPositiveInfinityTarget(constructor) {
var arr = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assertArrayEquals(expected, arr.copyWithin(+Infinity, 5));
assertEquals(10, arr.length);
});
CheckEachTypedArray(function copyWithinMinusInfinityStart(constructor) {
var arr = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var expected = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5];
assertArrayEquals(expected, arr.copyWithin(5, -Infinity));
assertEquals(10, arr.length);
});
CheckEachTypedArray(function copyWithinPositiveInfinityStart(constructor) {
var arr = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assertArrayEquals(expected, arr.copyWithin(5, +Infinity));
assertEquals(10, arr.length);
});
CheckEachTypedArray(function copyWithinMinusInfinityEnd(constructor) {
var arr = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assertArrayEquals(expected, arr.copyWithin(5, 0, -Infinity));
assertEquals(10, arr.length);
});
CheckEachTypedArray(function copyWithinPositiveInfinityEnd(constructor) {
var arr = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var expected = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5];
assertArrayEquals(expected, arr.copyWithin(5, 0, +Infinity));
assertEquals(10, arr.length);
});
...@@ -165,7 +165,6 @@ ...@@ -165,7 +165,6 @@
'built-ins/DataView/prototype/setInt16/detached-buffer-after-toindex-byteoffset': [FAIL], 'built-ins/DataView/prototype/setInt16/detached-buffer-after-toindex-byteoffset': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=4648 # https://bugs.chromium.org/p/v8/issues/detail?id=4648
'built-ins/TypedArray/prototype/copyWithin/detached-buffer': [FAIL],
'built-ins/TypedArray/prototype/every/detached-buffer': [FAIL], 'built-ins/TypedArray/prototype/every/detached-buffer': [FAIL],
'built-ins/TypedArray/prototype/fill/detached-buffer': [FAIL], 'built-ins/TypedArray/prototype/fill/detached-buffer': [FAIL],
'built-ins/TypedArray/prototype/filter/detached-buffer': [FAIL], 'built-ins/TypedArray/prototype/filter/detached-buffer': [FAIL],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment