[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:
parent
5cc8a2c50b
commit
03da2d8ce9
@ -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) \
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -618,6 +618,7 @@ TNode<Smi> CodeStubAssembler::ConvertToRelativeIndex(TNode<Context> context,
|
||||
Goto(&done);
|
||||
}
|
||||
BIND(&done);
|
||||
CSA_ASSERT(this, TaggedIsPositiveSmi(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user