From 03da2d8ce9bfb62b2e7dee6e89c988294d49ff8c Mon Sep 17 00:00:00 2001 From: Choongwoo Han Date: Tue, 30 Jan 2018 19:42:49 +0900 Subject: [PATCH] [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 Commit-Queue: Peter Marshall Reviewed-by: Peter Marshall Reviewed-by: Jakob Gruber Cr-Commit-Position: refs/heads/master@{#50952} --- src/builtins/builtins-definitions.h | 3 +- src/builtins/builtins-typedarray-gen.cc | 154 ++++++++++++++++++++++-- src/builtins/builtins-typedarray.cc | 54 --------- src/code-stub-assembler.cc | 1 + src/runtime/runtime-typedarray.cc | 16 +++ src/runtime/runtime.h | 1 + test/test262/test262.status | 3 - 7 files changed, 164 insertions(+), 68 deletions(-) diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index 4ae4149a6c..cdc535ded6 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -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) \ diff --git a/src/builtins/builtins-typedarray-gen.cc b/src/builtins/builtins-typedarray-gen.cc index cf4fb0d8e6..c815220dcf 100644 --- a/src/builtins/builtins-typedarray-gen.cc +++ b/src/builtins/builtins-typedarray-gen.cc @@ -77,8 +77,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { TNode exemplar); TNode TypedArraySpeciesConstructor(TNode context, - TNode exemplar, - TNode default_constructor); + TNode exemplar); TNode SpeciesCreateByArrayBuffer(TNode context, TNode exemplar, @@ -87,6 +86,11 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { TNode len, const char* method_name); + TNode SpeciesCreateByLength(TNode context, + TNode exemplar, + TNode len, + const char* method_name); + TNode GetBuffer(TNode context, TNode array); @@ -1007,11 +1011,14 @@ TNode TypedArrayBuiltinsAssembler::GetDefaultConstructor( } TNode TypedArrayBuiltinsAssembler::TypedArraySpeciesConstructor( - TNode context, TNode exemplar, - TNode default_constructor) { + TNode context, TNode 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 default_constructor = GetDefaultConstructor(context, exemplar); + var_constructor = default_constructor; Node* map = LoadMap(exemplar); GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow); @@ -1030,13 +1037,8 @@ TNode TypedArrayBuiltinsAssembler::SpeciesCreateByArrayBuffer( TNode context, TNode exemplar, TNode buffer, TNode byte_offset, TNode len, const char* method_name) { - // Let defaultConstructor be the intrinsic object listed in column one of - // Table 52 for exemplar.[[TypedArrayName]]. - TNode default_constructor = GetDefaultConstructor(context, exemplar); - // Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor). - TNode constructor = - TypedArraySpeciesConstructor(context, exemplar, default_constructor); + TNode constructor = TypedArraySpeciesConstructor(context, exemplar); // Let newTypedArray be ? Construct(constructor, argumentList). TNode new_object = @@ -1047,6 +1049,35 @@ TNode TypedArrayBuiltinsAssembler::SpeciesCreateByArrayBuffer( return ValidateTypedArray(context, new_object, method_name); } +TNode TypedArrayBuiltinsAssembler::SpeciesCreateByLength( + TNode context, TNode exemplar, TNode len, + const char* method_name) { + CSA_ASSERT(this, TaggedIsPositiveSmi(len)); + + // Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor). + TNode constructor = TypedArraySpeciesConstructor(context, exemplar); + CSA_ASSERT(this, IsJSFunction(constructor)); + + // Let newTypedArray be ? Construct(constructor, argumentList). + TNode new_object = CAST(ConstructJS(CodeFactory::Construct(isolate()), + context, constructor, len)); + + // Perform ? ValidateTypedArray(newTypedArray). + TNode 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 new_length = + LoadObjectField(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 TypedArrayBuiltinsAssembler::GetBuffer( TNode context, TNode 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 = CAST(Parameter(BuiltinDescriptor::kContext)); + CodeStubArguments args( + this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount))); + + TNode receiver = args.GetReceiver(); + TNode source = + ValidateTypedArray(context, receiver, method_name); + + TNode source_length = + LoadObjectField(source, JSTypedArray::kLengthOffset); + + // Convert start offset argument to integer, and calculate relative offset. + TNode start = args.GetOptionalArgumentValue(0, SmiConstant(0)); + TNode 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 end = args.GetOptionalArgumentValue(1, UndefinedConstant()); + TNode end_index = Select( + IsUndefined(end), [=] { return source_length; }, + [=] { return ConvertToRelativeIndex(context, end, source_length); }, + MachineRepresentation::kTagged); + + // Create a result array by invoking TypedArraySpeciesCreate. + TNode count = SmiMax(SmiSub(end_index, start_index), SmiConstant(0)); + TNode 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 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 source_el_kind = LoadElementsKind(source); + TNode target_el_kind = LoadElementsKind(result_array); + GotoIfNot(Word32Equal(source_el_kind, target_el_kind), &call_runtime); + + TNode target_buffer = + LoadObjectField(result_array, JSTypedArray::kBufferOffset); + Branch(WordEqual(receiver_buffer, target_buffer), &call_runtime, + &call_memmove); + + BIND(&call_memmove); + { + GotoIfForceSlowPath(&call_runtime); + + TNode target_data_ptr = + UncheckedCast(LoadDataPtr(result_array)); + TNode source_data_ptr = + UncheckedCast(LoadDataPtr(source)); + + TNode source_el_size = GetTypedArrayElementSize(source_el_kind); + TNode source_start_bytes = + IntPtrMul(SmiToWord(start_index), source_el_size); + TNode source_start = + IntPtrAdd(source_data_ptr, source_start_bytes); + + TNode count_bytes = IntPtrMul(SmiToWord(count), source_el_size); + +#ifdef DEBUG + TNode target_byte_length = + LoadAndUntagObjectField(result_array, JSTypedArray::kByteLengthOffset); + CSA_ASSERT(this, IntPtrLessThanOrEqual(count_bytes, target_byte_length)); + + TNode source_byte_length = + LoadAndUntagObjectField(source, JSTypedArray::kByteLengthOffset); + TNode 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"; diff --git a/src/builtins/builtins-typedarray.cc b/src/builtins/builtins-typedarray.cc index 18625c8d90..aa5f52170b 100644 --- a/src/builtins/builtins-typedarray.cc +++ b/src/builtins/builtins-typedarray.cc @@ -42,16 +42,6 @@ int64_t CapRelativeIndex(Handle num, int64_t minimum, int64_t maximum) { : std::min(relative, maximum); } -MaybeHandle TypedArraySpeciesCreateByLength( - Isolate* isolate, Handle exemplar, const char* method_name, - int64_t length) { - const int argc = 1; - ScopedVector> 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 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 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(end - start, 0); - - Handle 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(start), - static_cast(end), result_array); -} - } // namespace internal } // namespace v8 diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc index 039e98f94c..cb1fa36662 100644 --- a/src/code-stub-assembler.cc +++ b/src/code-stub-assembler.cc @@ -618,6 +618,7 @@ TNode CodeStubAssembler::ConvertToRelativeIndex(TNode context, Goto(&done); } BIND(&done); + CSA_ASSERT(this, TaggedIsPositiveSmi(result)); return result; } diff --git a/src/runtime/runtime-typedarray.cc b/src/runtime/runtime-typedarray.cc index d18b7d9cb2..e1677f42b7 100644 --- a/src/runtime/runtime-typedarray.cc +++ b/src/runtime/runtime-typedarray.cc @@ -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 source = args.at(0); + Handle start = args.at(1); + Handle end = args.at(2); + Handle result = args.at(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 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 07562d1487..1f7d2f841d 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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) \ diff --git a/test/test262/test262.status b/test/test262/test262.status index 38fe454e4e..247a0a0a7f 100644 --- a/test/test262/test262.status +++ b/test/test262/test262.status @@ -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