[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}
This commit is contained in:
parent
f22979bca0
commit
c5c0765ad9
@ -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,
|
||||
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,7 +3543,7 @@ class FastSloppyArgumentsElementsAccessor
|
||||
return Handle<FixedArray>(FixedArray::cast(parameter_map->get(1)), isolate);
|
||||
}
|
||||
|
||||
static Handle<JSArray> SliceImpl(Handle<JSObject> receiver, uint32_t start,
|
||||
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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user