Commit c5c0765a authored by cwhan.tunz's avatar cwhan.tunz Committed by Commit bot

[typedarrays] Move %TypedArray%.prototype.slice to C++

- Implement %TypedArray%.prototype.slice to C++ builtins
- Remove TypedArraySlice in src/js/typedarray.js
- Implement TypedArraySpeciesCreate in builtins-typedarray.cc
- Implement TypedArrayCreate in builtins-typedarray.cc

BUG=v8:5929

Review-Url: https://codereview.chromium.org/2763473002
Cr-Commit-Position: refs/heads/master@{#44322}
parent f22979bc
......@@ -2750,6 +2750,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kTypedArrayPrototypeLastIndexOf, 1, false);
SimpleInstallFunction(prototype, "reverse",
Builtins::kTypedArrayPrototypeReverse, 0, false);
SimpleInstallFunction(prototype, "slice",
Builtins::kTypedArrayPrototypeSlice, 2, false);
}
{ // -- T y p e d A r r a y s
......
......@@ -892,6 +892,8 @@ namespace internal {
CPP(TypedArrayPrototypeLastIndexOf) \
/* ES6 #sec-%typedarray%.prototype.reverse */ \
CPP(TypedArrayPrototypeReverse) \
/* ES6 #sec-%typedarray%.prototype.slice */ \
CPP(TypedArrayPrototypeSlice) \
/* ES6 %TypedArray%.prototype.every */ \
TFJ(TypedArrayPrototypeEvery, 2, kCallbackFn, kThisArg) \
/* ES6 %TypedArray%.prototype.some */ \
......
......@@ -41,6 +41,83 @@ int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
: std::min<int64_t>(relative, maximum);
}
// ES7 section 22.2.4.6 TypedArrayCreate ( constructor, argumentList )
MaybeHandle<JSTypedArray> TypedArrayCreate(Isolate* isolate,
Handle<JSFunction> default_ctor,
int argc, Handle<Object>* argv,
const char* method_name) {
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
Handle<Object> new_obj;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, new_obj, Execution::New(default_ctor, argc, argv), JSTypedArray);
// 2. Perform ? ValidateTypedArray(newTypedArray).
Handle<JSTypedArray> new_array;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, new_array, JSTypedArray::Validate(isolate, new_obj, method_name),
JSTypedArray);
// 3. If argumentList is a List of a single Number, then
// If newTypedArray.[[ArrayLength]] < size, throw a TypeError exception.
DCHECK_IMPLIES(argc == 1, argv[0]->IsSmi());
if (argc == 1 && new_array->length_value() < argv[0]->Number()) {
const MessageTemplate::Template message =
MessageTemplate::kTypedArrayTooShort;
THROW_NEW_ERROR(isolate, NewTypeError(message), JSTypedArray);
}
// 4. Return newTypedArray.
return new_array;
}
// ES7 section 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList )
MaybeHandle<JSTypedArray> TypedArraySpeciesCreate(Isolate* isolate,
Handle<JSTypedArray> exemplar,
int argc,
Handle<Object>* argv,
const char* method_name) {
// 1. Assert: exemplar is an Object that has a [[TypedArrayName]] internal
// slot.
DCHECK(exemplar->IsJSTypedArray());
// 2. Let defaultConstructor be the intrinsic object listed in column one of
// Table 51 for exemplar.[[TypedArrayName]].
Handle<JSFunction> default_ctor = isolate->uint8_array_fun();
switch (exemplar->type()) {
#define TYPED_ARRAY_CTOR(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: { \
default_ctor = isolate->type##_array_fun(); \
break; \
}
TYPED_ARRAYS(TYPED_ARRAY_CTOR)
#undef TYPED_ARRAY_CTOR
default:
UNREACHABLE();
}
// 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
Handle<Object> ctor;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, ctor,
Object::SpeciesConstructor(isolate, exemplar, default_ctor),
JSTypedArray);
// 4. Return ? TypedArrayCreate(constructor, argumentList).
return TypedArrayCreate(isolate, Handle<JSFunction>::cast(ctor), argc, argv,
method_name);
}
MaybeHandle<JSTypedArray> TypedArraySpeciesCreateByLength(
Isolate* isolate, Handle<JSTypedArray> exemplar, const char* method_name,
int64_t length) {
const int argc = 1;
ScopedVector<Handle<Object>> argv(argc);
argv[0] = isolate->factory()->NewNumberFromInt64(length);
return TypedArraySpeciesCreate(isolate, exemplar, argc, argv.start(),
method_name);
}
} // namespace
BUILTIN(TypedArrayPrototypeCopyWithin) {
......@@ -270,5 +347,55 @@ BUILTIN(TypedArrayPrototypeReverse) {
return *array;
}
BUILTIN(TypedArrayPrototypeSlice) {
HandleScope scope(isolate);
Handle<JSTypedArray> array;
const char* method = "%TypedArray%.prototype.slice";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
int64_t len = array->length_value();
int64_t start = 0;
int64_t end = len;
{
Handle<Object> num = args.atOrUndefined(isolate, 1);
if (!num->IsUndefined(isolate)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
Object::ToInteger(isolate, num));
start = CapRelativeIndex(num, 0, len);
num = args.atOrUndefined(isolate, 2);
if (!num->IsUndefined(isolate)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
Object::ToInteger(isolate, num));
end = CapRelativeIndex(num, 0, len);
}
}
}
int64_t count = std::max<int64_t>(end - start, 0);
Handle<JSTypedArray> result_array;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result_array,
TypedArraySpeciesCreateByLength(isolate, array, method, count));
// TODO(cwhan.tunz): neutering check of the result_array should be done in
// TypedArraySpeciesCreate, but currently ValidateTypedArray does not throw
// for neutered buffer, so this is a temporary neutering check for the result
// array
if (V8_UNLIKELY(result_array->WasNeutered())) return *result_array;
// TODO(cwhan.tunz): should throw.
if (V8_UNLIKELY(array->WasNeutered())) return *result_array;
if (count == 0) return *result_array;
ElementsAccessor* accessor = array->GetElementsAccessor();
return *accessor->Slice(array, static_cast<uint32_t>(start),
static_cast<uint32_t>(end), result_array);
}
} // namespace internal
} // namespace v8
......@@ -700,15 +700,27 @@ class ElementsAccessorBase : public ElementsAccessor {
return 0;
}
Handle<JSArray> Slice(Handle<JSObject> receiver, uint32_t start,
uint32_t end) final {
Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
uint32_t end) final {
return Subclass::SliceImpl(receiver, start, end);
}
static Handle<JSArray> SliceImpl(Handle<JSObject> receiver,
uint32_t start, uint32_t end) {
Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
uint32_t end, Handle<JSObject> result) final {
return Subclass::SliceWithResultImpl(receiver, start, end, result);
}
static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
uint32_t end) {
UNREACHABLE();
return Handle<JSArray>();
return Handle<JSObject>();
}
static Handle<JSObject> SliceWithResultImpl(Handle<JSObject> receiver,
uint32_t start, uint32_t end,
Handle<JSObject> result) {
UNREACHABLE();
return Handle<JSObject>();
}
Handle<JSArray> Splice(Handle<JSArray> receiver, uint32_t start,
......@@ -2049,8 +2061,8 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
AT_START);
}
static Handle<JSArray> SliceImpl(Handle<JSObject> receiver,
uint32_t start, uint32_t end) {
static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
uint32_t end) {
Isolate* isolate = receiver->GetIsolate();
Handle<FixedArrayBase> backing_store(receiver->elements(), isolate);
int result_len = end < start ? 0u : end - start;
......@@ -3052,6 +3064,48 @@ class TypedElementsAccessor
ctype* data = static_cast<ctype*>(elements->DataPtr());
std::reverse(data, data + len);
}
static Handle<JSObject> SliceWithResultImpl(Handle<JSObject> receiver,
uint32_t start, uint32_t end,
Handle<JSObject> result) {
Isolate* isolate = receiver->GetIsolate();
DCHECK(!WasNeutered(*receiver));
DCHECK(result->IsJSTypedArray());
DCHECK(!WasNeutered(*result));
DCHECK_LE(start, end);
Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(receiver);
Handle<JSTypedArray> result_array = Handle<JSTypedArray>::cast(result);
DCHECK_LE(end, array->length_value());
// Fast path for the same type result array
if (result_array->type() == array->type()) {
int64_t element_size = array->element_size();
int64_t count = end - start;
DisallowHeapAllocation no_gc;
BackingStore* src_elements = BackingStore::cast(receiver->elements());
BackingStore* result_elements =
BackingStore::cast(result_array->elements());
DCHECK_LE(count, result_elements->length());
uint8_t* src = static_cast<uint8_t*>(src_elements->DataPtr());
uint8_t* result = static_cast<uint8_t*>(result_elements->DataPtr());
std::memcpy(result, src + start * element_size, count * element_size);
return result_array;
}
// If the types of the two typed arrays are different, properly convert
// elements
Handle<BackingStore> from(BackingStore::cast(array->elements()), isolate);
ElementsAccessor* result_accessor = result_array->GetElementsAccessor();
for (uint32_t i = start; i < end; i++) {
Handle<Object> elem = AccessorClass::GetImpl(isolate, *from, i);
result_accessor->Set(result_array, i, *elem);
}
return result_array;
}
};
#define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype, size) \
......@@ -3489,8 +3543,8 @@ class FastSloppyArgumentsElementsAccessor
return Handle<FixedArray>(FixedArray::cast(parameter_map->get(1)), isolate);
}
static Handle<JSArray> SliceImpl(Handle<JSObject> receiver, uint32_t start,
uint32_t end) {
static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
uint32_t end) {
Isolate* isolate = receiver->GetIsolate();
uint32_t result_len = end < start ? 0u : end - start;
Handle<JSArray> result_array = isolate->factory()->NewJSArray(
......
......@@ -142,8 +142,11 @@ class ElementsAccessor {
virtual uint32_t Unshift(Handle<JSArray> receiver,
Arguments* args, uint32_t unshift_size) = 0;
virtual Handle<JSArray> Slice(Handle<JSObject> receiver,
uint32_t start, uint32_t end) = 0;
virtual Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
uint32_t end) = 0;
virtual Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
uint32_t end, Handle<JSObject> result) = 0;
virtual Handle<JSArray> Splice(Handle<JSArray> receiver,
uint32_t start, uint32_t delete_count,
......
......@@ -583,49 +583,6 @@ function TypedArrayReduceRight(callback, current) {
%FunctionSetLength(TypedArrayReduceRight, 1);
function TypedArraySlice(start, end) {
if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
var len = %_TypedArrayGetLength(this);
var relativeStart = TO_INTEGER(start);
var k;
if (relativeStart < 0) {
k = MaxSimple(len + relativeStart, 0);
} else {
k = MinSimple(relativeStart, len);
}
var relativeEnd;
if (IS_UNDEFINED(end)) {
relativeEnd = len;
} else {
relativeEnd = TO_INTEGER(end);
}
var final;
if (relativeEnd < 0) {
final = MaxSimple(len + relativeEnd, 0);
} else {
final = MinSimple(relativeEnd, len);
}
var count = MaxSimple(final - k, 0);
var array = TypedArraySpeciesCreate(this, count);
// The code below is the 'then' branch; the 'else' branch species
// a memcpy. Because V8 doesn't canonicalize NaN, the difference is
// unobservable.
var n = 0;
while (k < final) {
var kValue = this[k];
array[n] = kValue;
k++;
n++;
}
return array;
}
// ES6 draft 08-24-14, section 22.2.2.2
function TypedArrayOf() {
var length = arguments.length;
......@@ -710,7 +667,6 @@ utils.InstallFunctions(GlobalTypedArray.prototype, DONT_ENUM, [
"map", TypedArrayMap,
"reduce", TypedArrayReduce,
"reduceRight", TypedArrayReduceRight,
"slice", TypedArraySlice,
"some", TypedArraySome,
"sort", TypedArraySort,
"toLocaleString", TypedArrayToLocaleString
......
......@@ -68,4 +68,28 @@ for (var constructor of typedArrayConstructors) {
assertEquals(2, slice[0]);
assertEquals(3, slice[1]);
assertTrue(slice instanceof constructor);
// Check that the species array must be a typed array
class MyTypedArray extends constructor {
static get[Symbol.species]() {
return Array;
}
}
var arr = new MyTypedArray([-1.0, 0, 1.1, 255, 256]);
assertThrows(() => arr.slice(), TypeError);
}
// Check that the result array is properly created by checking species
for (var constructor1 of typedArrayConstructors) {
for (var constructor2 of typedArrayConstructors) {
class MyTypedArray2 extends constructor1 {
static get[Symbol.species]() {
return constructor2;
}
}
var arr = new MyTypedArray2([-1.0, 0, 1.1, 255, 256]);
var arr2 = new constructor1([-1.0, 0, 1.1, 255, 256]);
assertEquals(new constructor2(arr2), arr.slice(),
constructor1.name + ' -> ' + constructor2.name);
}
}
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