[typedarray] Reimplement TA.p.slice in CSA

- Port TypedArray.prototype.slice to CSA
- Implement TypedArraySpeciesCreateByLength as a CSA
- Fix spec bugs: Throw if a source typed array is neutered
 after creating a result typed array

Bug: v8:5929
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ia7ce2239d37db6db172c00aa120ef51c31a14bac
Reviewed-on: https://chromium-review.googlesource.com/830991
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50952}
This commit is contained in:
Choongwoo Han 2018-01-30 19:42:49 +09:00 committed by Commit Bot
parent 5cc8a2c50b
commit 03da2d8ce9
7 changed files with 164 additions and 68 deletions

View File

@ -1102,7 +1102,8 @@ namespace internal {
/* ES6 %TypedArray%.prototype.set */ \
TFJ(TypedArrayPrototypeSet, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-%typedarray%.prototype.slice */ \
CPP(TypedArrayPrototypeSlice) \
TFJ(TypedArrayPrototypeSlice, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 %TypedArray%.prototype.subarray */ \
TFJ(TypedArrayPrototypeSubArray, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \

View File

@ -77,8 +77,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<JSTypedArray> exemplar);
TNode<Object> TypedArraySpeciesConstructor(TNode<Context> context,
TNode<JSTypedArray> exemplar,
TNode<Object> default_constructor);
TNode<JSTypedArray> exemplar);
TNode<JSTypedArray> SpeciesCreateByArrayBuffer(TNode<Context> context,
TNode<JSTypedArray> exemplar,
@ -87,6 +86,11 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<Smi> len,
const char* method_name);
TNode<JSTypedArray> SpeciesCreateByLength(TNode<Context> context,
TNode<JSTypedArray> exemplar,
TNode<Smi> len,
const char* method_name);
TNode<JSArrayBuffer> GetBuffer(TNode<Context> context,
TNode<JSTypedArray> array);
@ -1007,11 +1011,14 @@ TNode<Object> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
}
TNode<Object> TypedArrayBuiltinsAssembler::TypedArraySpeciesConstructor(
TNode<Context> context, TNode<JSTypedArray> exemplar,
TNode<Object> default_constructor) {
TNode<Context> context, TNode<JSTypedArray> exemplar) {
TVARIABLE(Object, var_constructor);
Label slow(this), done(this);
// Let defaultConstructor be the intrinsic object listed in column one of
// Table 52 for exemplar.[[TypedArrayName]].
TNode<Object> default_constructor = GetDefaultConstructor(context, exemplar);
var_constructor = default_constructor;
Node* map = LoadMap(exemplar);
GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow);
@ -1030,13 +1037,8 @@ TNode<JSTypedArray> TypedArrayBuiltinsAssembler::SpeciesCreateByArrayBuffer(
TNode<Context> context, TNode<JSTypedArray> exemplar,
TNode<JSArrayBuffer> buffer, TNode<Number> byte_offset, TNode<Smi> len,
const char* method_name) {
// Let defaultConstructor be the intrinsic object listed in column one of
// Table 52 for exemplar.[[TypedArrayName]].
TNode<Object> default_constructor = GetDefaultConstructor(context, exemplar);
// Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
TNode<Object> constructor =
TypedArraySpeciesConstructor(context, exemplar, default_constructor);
TNode<Object> constructor = TypedArraySpeciesConstructor(context, exemplar);
// Let newTypedArray be ? Construct(constructor, argumentList).
TNode<Object> new_object =
@ -1047,6 +1049,35 @@ TNode<JSTypedArray> TypedArrayBuiltinsAssembler::SpeciesCreateByArrayBuffer(
return ValidateTypedArray(context, new_object, method_name);
}
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::SpeciesCreateByLength(
TNode<Context> context, TNode<JSTypedArray> exemplar, TNode<Smi> len,
const char* method_name) {
CSA_ASSERT(this, TaggedIsPositiveSmi(len));
// Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
TNode<Object> constructor = TypedArraySpeciesConstructor(context, exemplar);
CSA_ASSERT(this, IsJSFunction(constructor));
// Let newTypedArray be ? Construct(constructor, argumentList).
TNode<Object> new_object = CAST(ConstructJS(CodeFactory::Construct(isolate()),
context, constructor, len));
// Perform ? ValidateTypedArray(newTypedArray).
TNode<JSTypedArray> new_typed_array =
ValidateTypedArray(context, new_object, method_name);
// If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError
// exception.
Label if_length_is_not_short(this);
TNode<Smi> new_length =
LoadObjectField<Smi>(new_typed_array, JSTypedArray::kLengthOffset);
GotoIfNot(SmiLessThan(new_length, len), &if_length_is_not_short);
ThrowTypeError(context, MessageTemplate::kNotTypedArray);
BIND(&if_length_is_not_short);
return new_typed_array;
}
TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
TNode<Context> context, TNode<JSTypedArray> array) {
Label call_runtime(this), done(this);
@ -1325,6 +1356,109 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
ThrowTypeError(context, MessageTemplate::kNotTypedArray);
}
// ES %TypedArray%.prototype.slice
TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
const char* method_name = "%TypedArray%.prototype.slice";
Label call_runtime(this), call_memmove(this), if_count_is_not_zero(this),
if_typed_array_is_neutered(this, Label::kDeferred);
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
CodeStubArguments args(
this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
TNode<Object> receiver = args.GetReceiver();
TNode<JSTypedArray> source =
ValidateTypedArray(context, receiver, method_name);
TNode<Smi> source_length =
LoadObjectField<Smi>(source, JSTypedArray::kLengthOffset);
// Convert start offset argument to integer, and calculate relative offset.
TNode<Object> start = args.GetOptionalArgumentValue(0, SmiConstant(0));
TNode<Smi> start_index =
ConvertToRelativeIndex(context, start, source_length);
// Convert end offset argument to integer, and calculate relative offset.
// If end offset is not given or undefined is given, set source_length to
// "end_index".
TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
TNode<Smi> end_index = Select<Smi>(
IsUndefined(end), [=] { return source_length; },
[=] { return ConvertToRelativeIndex(context, end, source_length); },
MachineRepresentation::kTagged);
// Create a result array by invoking TypedArraySpeciesCreate.
TNode<Smi> count = SmiMax(SmiSub(end_index, start_index), SmiConstant(0));
TNode<JSTypedArray> result_array =
SpeciesCreateByLength(context, source, count, method_name);
// If count is zero, return early.
GotoIf(SmiGreaterThan(count, SmiConstant(0)), &if_count_is_not_zero);
args.PopAndReturn(result_array);
BIND(&if_count_is_not_zero);
// Check the source array is neutered or not. We don't need to check if the
// result array is neutered or not since TypedArraySpeciesCreate checked it.
CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(LoadObjectField(
result_array, JSTypedArray::kBufferOffset))));
TNode<Object> receiver_buffer =
LoadObjectField(CAST(receiver), JSTypedArray::kBufferOffset);
GotoIf(IsDetachedBuffer(receiver_buffer), &if_typed_array_is_neutered);
// result_array could be a different type from source or share the same
// buffer with the source because of custom species constructor.
// If the types of source and result array are the same and they are not
// sharing the same buffer, use memmove.
TNode<Word32T> source_el_kind = LoadElementsKind(source);
TNode<Word32T> target_el_kind = LoadElementsKind(result_array);
GotoIfNot(Word32Equal(source_el_kind, target_el_kind), &call_runtime);
TNode<Object> target_buffer =
LoadObjectField(result_array, JSTypedArray::kBufferOffset);
Branch(WordEqual(receiver_buffer, target_buffer), &call_runtime,
&call_memmove);
BIND(&call_memmove);
{
GotoIfForceSlowPath(&call_runtime);
TNode<IntPtrT> target_data_ptr =
UncheckedCast<IntPtrT>(LoadDataPtr(result_array));
TNode<IntPtrT> source_data_ptr =
UncheckedCast<IntPtrT>(LoadDataPtr(source));
TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
TNode<IntPtrT> source_start_bytes =
IntPtrMul(SmiToWord(start_index), source_el_size);
TNode<IntPtrT> source_start =
IntPtrAdd(source_data_ptr, source_start_bytes);
TNode<IntPtrT> count_bytes = IntPtrMul(SmiToWord(count), source_el_size);
#ifdef DEBUG
TNode<IntPtrT> target_byte_length =
LoadAndUntagObjectField(result_array, JSTypedArray::kByteLengthOffset);
CSA_ASSERT(this, IntPtrLessThanOrEqual(count_bytes, target_byte_length));
TNode<IntPtrT> source_byte_length =
LoadAndUntagObjectField(source, JSTypedArray::kByteLengthOffset);
TNode<IntPtrT> source_size_in_bytes =
IntPtrSub(source_byte_length, source_start_bytes);
CSA_ASSERT(this, IntPtrLessThanOrEqual(count_bytes, source_size_in_bytes));
#endif // DEBUG
CallCMemmove(target_data_ptr, source_start, count_bytes);
args.PopAndReturn(result_array);
}
BIND(&call_runtime);
args.PopAndReturn(CallRuntime(Runtime::kTypedArraySlice, context, source,
start_index, end_index, result_array));
BIND(&if_typed_array_is_neutered);
ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
}
// ES %TypedArray%.prototype.subarray
TF_BUILTIN(TypedArrayPrototypeSubArray, TypedArrayBuiltinsAssembler) {
const char* method_name = "%TypedArray%.prototype.subarray";

View File

@ -42,16 +42,6 @@ int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
: std::min<int64_t>(relative, maximum);
}
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 JSTypedArray::SpeciesCreate(isolate, exemplar, argc, argv.start(),
method_name);
}
} // namespace
BUILTIN(TypedArrayPrototypeCopyWithin) {
@ -277,49 +267,5 @@ 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): 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

View File

@ -618,6 +618,7 @@ TNode<Smi> CodeStubAssembler::ConvertToRelativeIndex(TNode<Context> context,
Goto(&done);
}
BIND(&done);
CSA_ASSERT(this, TaggedIsPositiveSmi(result));
return result;
}

View File

@ -258,5 +258,21 @@ RUNTIME_FUNCTION(Runtime_TypedArraySet) {
return accessor->CopyElements(source, target, int_l, uint_offset);
}
// 22.2.3.4 %TypedArray%.prototype.slice ( start, end )
RUNTIME_FUNCTION(Runtime_TypedArraySlice) {
HandleScope scope(isolate);
Handle<JSTypedArray> source = args.at<JSTypedArray>(0);
Handle<Smi> start = args.at<Smi>(1);
Handle<Smi> end = args.at<Smi>(2);
Handle<JSTypedArray> result = args.at<JSTypedArray>(3);
DCHECK(!source->WasNeutered());
DCHECK(!result->WasNeutered());
DCHECK_LE(start->value(), end->value());
ElementsAccessor* accessor = source->GetElementsAccessor();
return *accessor->Slice(source, start->value(), end->value(), result);
}
} // namespace internal
} // namespace v8

View File

@ -642,6 +642,7 @@ namespace internal {
F(TypedArrayGetBuffer, 1, 1) \
F(TypedArraySortFast, 1, 1) \
F(TypedArraySet, 2, 1) \
F(TypedArraySlice, 4, 1) \
F(IsTypedArray, 1, 1) \
F(IsSharedTypedArray, 1, 1) \
F(IsSharedIntegerTypedArray, 1, 1) \

View File

@ -106,9 +106,6 @@
'built-ins/TypedArray/prototype/map/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/slice/detached-buffer-custom-ctor-other-targettype': [FAIL],
'built-ins/TypedArray/prototype/slice/detached-buffer-custom-ctor-same-targettype': [FAIL],
'built-ins/TypedArray/prototype/slice/detached-buffer-get-ctor': [FAIL],
'built-ins/TypedArray/prototype/some/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/sort/detached-buffer-comparefn': [FAIL],
# DataView functions should also throw on detached buffers