Commit dd15e30b authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[typedarray] Widen set fast path for JSTypedArray source arguments

Pull most JSTypedArray source arguments onto the fast path.

Same source & target elements-kinds simply call memmove. Other
combinations call directly into C and reuse ElementsAccessor logic.

Only overlapping source & target args with differing elements-kinds
remain on the slow runtime path.

Bug: v8:7123, v8:3590
Cq-Include-Trybots: master.tryserver.v8:v8_linux_noi18n_rel_ng
Change-Id: I80284b61478b0e3266b8f16bde8a56bd90f080b0
Reviewed-on: https://chromium-review.googlesource.com/788857
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49643}
parent f2150dbd
......@@ -1439,6 +1439,12 @@ ExternalReference::copy_fast_number_jsarray_elements_to_typed_array(
isolate, FUNCTION_ADDR(CopyFastNumberJSArrayElementsToTypedArray)));
}
ExternalReference ExternalReference::copy_typed_array_elements_to_typed_array(
Isolate* isolate) {
return ExternalReference(
Redirect(isolate, FUNCTION_ADDR(CopyTypedArrayElementsToTypedArray)));
}
ExternalReference ExternalReference::try_internalize_string_function(
Isolate* isolate) {
return ExternalReference(Redirect(
......
......@@ -990,6 +990,8 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference copy_fast_number_jsarray_elements_to_typed_array(
Isolate* isolate);
static ExternalReference copy_typed_array_elements_to_typed_array(
Isolate* isolate);
static ExternalReference page_flags(Page* page);
......
......@@ -53,8 +53,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<IntPtrT> GetTypedArrayElementSize(TNode<Word32T> elements_kind);
// Fast path for setting a TypedArray (source) onto another TypedArray
// (target) at an element offset. Currently, only handles when the source and
// target types match.
// (target) at an element offset.
void SetTypedArraySource(TNode<Context> context, TNode<JSTypedArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset,
Label* call_runtime, Label* if_source_too_large);
......@@ -69,6 +68,11 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
void CallCCopyFastNumberJSArrayElementsToTypedArray(
TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length, TNode<IntPtrT> offset);
void CallCCopyTypedArrayElementsToTypedArray(TNode<JSTypedArray> source,
TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length,
TNode<IntPtrT> offset);
};
Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) {
......@@ -765,33 +769,84 @@ void TypedArrayBuiltinsAssembler::SetTypedArraySource(
TNode<Context> context, TNode<JSTypedArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime,
Label* if_source_too_large) {
CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(
LoadObjectField(source, JSTypedArray::kBufferOffset))));
CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(
LoadObjectField(target, JSTypedArray::kBufferOffset))));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
CSA_ASSERT(this,
IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
// TODO(pwong): Widen this fast path. See CopyElementsHandleFromTypedArray for
// the corresponding check in runtime.
TNode<Word32T> source_el_kind = LoadElementsKind(source);
TNode<Word32T> target_el_kind = LoadElementsKind(target);
GotoIfNot(Word32Equal(source_el_kind, target_el_kind), call_runtime);
// Check for possible range errors.
TNode<IntPtrT> source_byte_length =
LoadAndUntagObjectField(source, JSTypedArray::kByteLengthOffset);
TNode<IntPtrT> target_byte_length =
LoadAndUntagObjectField(target, JSTypedArray::kByteLengthOffset);
TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
TNode<IntPtrT> required_byte_length =
IntPtrAdd(IntPtrMul(offset, source_el_size), source_byte_length);
GotoIf(IntPtrGreaterThan(required_byte_length, target_byte_length),
TNode<IntPtrT> source_length =
LoadAndUntagObjectField(source, JSTypedArray::kLengthOffset);
TNode<IntPtrT> target_length =
LoadAndUntagObjectField(target, JSTypedArray::kLengthOffset);
TNode<IntPtrT> required_target_length = IntPtrAdd(source_length, offset);
GotoIf(IntPtrGreaterThan(required_target_length, target_length),
if_source_too_large);
// If source and target are the same TypedArray type.
TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
// Grab pointers and byte lengths we need later on.
TNode<IntPtrT> target_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(target));
TNode<IntPtrT> source_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(source));
TNode<IntPtrT> target_start =
IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
CallCMemmove(target_start, source_data_ptr, source_byte_length);
TNode<Word32T> source_el_kind = LoadElementsKind(source);
TNode<Word32T> target_el_kind = LoadElementsKind(target);
TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
// A note on byte lengths: both source- and target byte lengths must be valid,
// i.e. it must be possible to allocate an array of the given length. That
// means we're safe from overflows in the following multiplication.
TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size);
CSA_ASSERT(this,
IntPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0)));
Label call_memmove(this), fast_c_call(this), out(this);
Branch(Word32Equal(source_el_kind, target_el_kind), &call_memmove,
&fast_c_call);
BIND(&call_memmove);
{
TNode<IntPtrT> target_start =
IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
CallCMemmove(target_start, source_data_ptr, source_byte_length);
Goto(&out);
}
BIND(&fast_c_call);
{
// Overlapping backing stores of different element kinds are handled in
// runtime. We're a bit conservative here and bail to runtime if ranges
// overlap and element kinds differ.
TNode<IntPtrT> target_byte_length =
IntPtrMul(target_length, target_el_size);
CSA_ASSERT(this,
IntPtrGreaterThanOrEqual(target_byte_length, IntPtrConstant(0)));
TNode<IntPtrT> target_data_end_ptr =
IntPtrAdd(target_data_ptr, target_byte_length);
TNode<IntPtrT> source_data_end_ptr =
IntPtrAdd(source_data_ptr, source_byte_length);
GotoIfNot(
Word32Or(IntPtrLessThanOrEqual(target_data_end_ptr, source_data_ptr),
IntPtrLessThanOrEqual(source_data_end_ptr, target_data_ptr)),
call_runtime);
TNode<IntPtrT> source_length =
LoadAndUntagObjectField(source, JSTypedArray::kLengthOffset);
CallCCopyTypedArrayElementsToTypedArray(source, target, source_length,
offset);
Goto(&out);
}
BIND(&out);
}
void TypedArrayBuiltinsAssembler::SetJSArraySource(
......@@ -863,6 +918,17 @@ void TypedArrayBuiltinsAssembler::
source, dest, source_length, offset);
}
void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
TNode<JSTypedArray> source, TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length, TNode<IntPtrT> offset) {
TNode<ExternalReference> f = ExternalConstant(
ExternalReference::copy_typed_array_elements_to_typed_array(isolate()));
CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::AnyTagged(), MachineType::UintPtr(),
MachineType::UintPtr(), f, source, dest, source_length,
offset);
}
// ES #sec-get-%typedarray%.prototype.set
TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
......@@ -872,7 +938,7 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this),
if_offset_is_out_of_bounds(this, Label::kDeferred),
if_source_too_large(this, Label::kDeferred),
if_receiver_is_neutered(this, Label::kDeferred),
if_typed_array_is_neutered(this, Label::kDeferred),
if_receiver_is_not_typedarray(this, Label::kDeferred);
// Check the receiver is a typed array.
......@@ -888,13 +954,17 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
// Since ToInteger always returns a Smi if the given value is within Smi
// range, and the only corner case of -0.0 has already been truncated to 0.0,
// we can simply throw unless the offset is a non-negative Smi.
// TODO(jgruber): It's an observable spec violation to throw here if
// {offset_num} is a positive number outside the Smi range. Per spec, we need
// to check for detached buffers and call the observable ToObject/ToLength
// operations first.
GotoIfNot(TaggedIsPositiveSmi(offset_num), &if_offset_is_out_of_bounds);
TNode<Smi> offset_smi = CAST(offset_num);
// Check the receiver is not neutered.
TNode<Object> receiver_buffer =
LoadObjectField(CAST(receiver), JSTypedArray::kBufferOffset);
GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiver_is_neutered);
GotoIf(IsDetachedBuffer(receiver_buffer), &if_typed_array_is_neutered);
// Check the source argument is valid and whether a fast path can be taken.
Label call_runtime(this);
......@@ -907,7 +977,11 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
// Fast path for a typed array source argument.
BIND(&if_source_is_typed_array);
{
CSA_ASSERT(this, IsJSTypedArray(source));
// Check the source argument is not neutered.
TNode<Object> source_buffer =
LoadObjectField(CAST(source), JSTypedArray::kBufferOffset);
GotoIf(IsDetachedBuffer(source_buffer), &if_typed_array_is_neutered);
SetTypedArraySource(context, CAST(source), CAST(receiver),
SmiUntag(offset_smi), &call_runtime,
&if_source_too_large);
......@@ -932,7 +1006,7 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
BIND(&if_source_too_large);
ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
BIND(&if_receiver_is_neutered);
BIND(&if_typed_array_is_neutered);
ThrowTypeError(context, MessageTemplate::kDetachedOperation,
"%TypedArray%.prototype.set");
......
......@@ -1184,6 +1184,15 @@ Node* CodeAssembler::CallCFunction3WithCallerSavedRegisters(
mode);
}
Node* CodeAssembler::CallCFunction4(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, Node* function, Node* arg0,
Node* arg1, Node* arg2, Node* arg3) {
return raw_assembler()->CallCFunction4(return_type, arg0_type, arg1_type,
arg2_type, arg3_type, function, arg0,
arg1, arg2, arg3);
}
Node* CodeAssembler::CallCFunction5(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
......
......@@ -1006,6 +1006,12 @@ class V8_EXPORT_PRIVATE CodeAssembler {
MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2,
SaveFPRegsMode mode);
// Call to a C function with four arguments.
Node* CallCFunction4(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
MachineType arg3_type, Node* function, Node* arg0,
Node* arg1, Node* arg2, Node* arg3);
// Call to a C function with five arguments.
Node* CallCFunction5(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
......
......@@ -301,6 +301,22 @@ Node* RawMachineAssembler::CallCFunction3WithCallerSavedRegisters(
arg0, arg1, arg2);
}
Node* RawMachineAssembler::CallCFunction4(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, Node* function, Node* arg0,
Node* arg1, Node* arg2, Node* arg3) {
MachineSignature::Builder builder(zone(), 1, 4);
builder.AddReturn(return_type);
builder.AddParam(arg0_type);
builder.AddParam(arg1_type);
builder.AddParam(arg2_type);
builder.AddParam(arg3_type);
const CallDescriptor* descriptor =
Linkage::GetSimplifiedCDescriptor(zone(), builder.Build());
return AddNode(common()->Call(descriptor), function, arg0, arg1, arg2, arg3);
}
Node* RawMachineAssembler::CallCFunction5(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
......
......@@ -778,6 +778,11 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2,
SaveFPRegsMode mode = kSaveFPRegs);
// Call to a C function with four arguments.
Node* CallCFunction4(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
MachineType arg3_type, Node* function, Node* arg0,
Node* arg1, Node* arg2, Node* arg3);
// Call to a C function with five arguments.
Node* CallCFunction5(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
......
......@@ -3206,18 +3206,18 @@ class TypedElementsAccessor
}
}
static void CopyElementsHandleFromTypedArray(Handle<JSTypedArray> source,
Handle<JSTypedArray> destination,
size_t length, uint32_t offset) {
static void CopyElementsFromTypedArray(JSTypedArray* source,
JSTypedArray* destination,
size_t length, uint32_t offset) {
// The source is a typed array, so we know we don't need to do ToNumber
// side-effects, as the source elements will always be a number or
// undefined.
DisallowHeapAllocation no_gc;
Handle<FixedTypedArrayBase> source_elements(
FixedTypedArrayBase::cast(source->elements()));
Handle<BackingStore> destination_elements(
BackingStore::cast(destination->elements()));
FixedTypedArrayBase* source_elements =
FixedTypedArrayBase::cast(source->elements());
BackingStore* destination_elements =
BackingStore::cast(destination->elements());
DCHECK_LE(offset + source->length(), destination->length());
DCHECK_GE(destination->length(), source->length());
......@@ -3255,10 +3255,10 @@ class TypedElementsAccessor
// We use scalar accessors below to avoid boxing/unboxing, so there are
// no allocations.
switch (source->GetElementsKind()) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
CopyBetweenBackingStores<Type##ArrayTraits>( \
*source_elements, *destination_elements, length, offset); \
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
CopyBetweenBackingStores<Type##ArrayTraits>( \
source_elements, destination_elements, length, offset); \
break;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
default:
......@@ -3408,8 +3408,7 @@ class TypedElementsAccessor
// All conversions from TypedArrays can be done without allocation.
if (source->IsJSTypedArray()) {
Handle<JSTypedArray> source_ta = Handle<JSTypedArray>::cast(source);
CopyElementsHandleFromTypedArray(source_ta, destination_ta, length,
offset);
CopyElementsFromTypedArray(*source_ta, *destination_ta, length, offset);
return *isolate->factory()->undefined_value();
}
......@@ -4387,6 +4386,22 @@ void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
}
}
void CopyTypedArrayElementsToTypedArray(JSTypedArray* source,
JSTypedArray* destination,
uintptr_t length, uintptr_t offset) {
switch (destination->GetElementsKind()) {
#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
Fixed##Type##ElementsAccessor::CopyElementsFromTypedArray( \
source, destination, length, static_cast<uint32_t>(offset)); \
break;
TYPED_ARRAYS(TYPED_ARRAYS_CASE)
#undef TYPED_ARRAYS_CASE
default:
UNREACHABLE();
}
}
void ElementsAccessor::InitializeOncePerProcess() {
static ElementsAccessor* accessor_array[] = {
#define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind),
......
......@@ -244,6 +244,9 @@ void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
JSTypedArray* destination,
uintptr_t length,
uintptr_t offset);
void CopyTypedArrayElementsToTypedArray(JSTypedArray* source,
JSTypedArray* destination,
uintptr_t length, uintptr_t offset);
} // namespace internal
} // namespace v8
......
......@@ -274,6 +274,9 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
isolate)
.address(),
"copy_fast_number_jsarray_elements_to_typed_array");
Add(ExternalReference::copy_typed_array_elements_to_typed_array(isolate)
.address(),
"copy_typed_array_elements_to_typed_array");
Add(ExternalReference::log_enter_external_function(isolate).address(),
"Logger::EnterExternal");
Add(ExternalReference::log_leave_external_function(isolate).address(),
......
......@@ -215,33 +215,38 @@ RUNTIME_FUNCTION(Runtime_TypedArraySpeciesCreateByLength) {
}
namespace {
Object* TypedArrayCopyElements(Handle<JSTypedArray> target,
Handle<JSReceiver> source, uint32_t length,
uint32_t offset) {
ElementsAccessor* accessor = target->GetElementsAccessor();
return accessor->CopyElements(source, target, length, offset);
}
enum class TypedArraySetResultCodes {
// Set from typed array of the different type, overlapping in memory.
OVERLAPPING,
// Set from typed array of the different type, non-overlapping.
NONOVERLAPPING,
// Set from non-typed array.
NON_TYPED_ARRAY,
};
MaybeHandle<Object> TypedArraySetFromOverlapping(Isolate* isolate,
Handle<JSTypedArray> target,
Handle<JSTypedArray> source,
uint32_t offset) {
DCHECK_GE(offset, 0);
Object* TypedArraySetFromOverlapping(Isolate* isolate,
Handle<JSTypedArray> target,
Handle<JSTypedArray> source,
uint32_t offset) {
#ifdef DEBUG
Handle<FixedTypedArrayBase> source_elements(
FixedTypedArrayBase::cast(source->elements()));
Handle<FixedTypedArrayBase> target_elements(
FixedTypedArrayBase::cast(target->elements()));
uint8_t* source_data = static_cast<uint8_t*>(source_elements->DataPtr());
uint8_t* target_data = static_cast<uint8_t*>(target_elements->DataPtr());
size_t source_byte_length = NumberToSize(source->byte_length());
size_t target_byte_length = NumberToSize(target->byte_length());
CHECK_LE(offset + source->length(), target->length());
CHECK_GE(target->length(), source->length());
CHECK(source->length()->IsSmi());
CHECK(!target->WasNeutered());
CHECK(!source->WasNeutered());
// Assert that target and source in fact overlapping.
CHECK(target_data + target_byte_length > source_data &&
source_data + source_byte_length > target_data);
#endif
size_t sourceElementSize = source->element_size();
size_t targetElementSize = target->element_size();
uint32_t source_length = source->length_value();
if (source_length == 0) return target;
if (source_length == 0) return isolate->heap()->undefined_value();
// Copy left part.
......@@ -299,62 +304,12 @@ MaybeHandle<Object> TypedArraySetFromOverlapping(Isolate* isolate,
target_accessor->Set(target, offset + i, *temp[i - left_index]);
}
return target;
}
MaybeHandle<Smi> TypedArraySetFastCases(Isolate* isolate,
Handle<JSTypedArray> target,
Handle<Object> source_obj,
Handle<Object> offset_obj) {
if (!source_obj->IsJSTypedArray()) {
return MaybeHandle<Smi>(
Smi::FromEnum(TypedArraySetResultCodes::NON_TYPED_ARRAY), isolate);
}
Handle<JSTypedArray> source = Handle<JSTypedArray>::cast(source_obj);
DCHECK_NE(target->type(), source->type()); // Handled in SetTypedArraySource.
size_t offset = 0;
CHECK(TryNumberToSize(*offset_obj, &offset));
size_t target_length = target->length_value();
size_t source_length = source->length_value();
size_t target_byte_length = NumberToSize(target->byte_length());
size_t source_byte_length = NumberToSize(source->byte_length());
if (offset > target_length || offset + source_length > target_length ||
offset + source_length < offset) { // overflow
THROW_NEW_ERROR(
isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge),
Smi);
}
size_t target_offset = NumberToSize(target->byte_offset());
size_t source_offset = NumberToSize(source->byte_offset());
uint8_t* target_base =
static_cast<uint8_t*>(target->GetBuffer()->backing_store()) +
target_offset;
uint8_t* source_base =
static_cast<uint8_t*>(source->GetBuffer()->backing_store()) +
source_offset;
// Typed arrays of different types over the same backing store
if ((source_base <= target_base &&
source_base + source_byte_length > target_base) ||
(target_base <= source_base &&
target_base + target_byte_length > source_base)) {
// We do not support overlapping ArrayBuffers
DCHECK(target->GetBuffer()->backing_store() ==
source->GetBuffer()->backing_store());
return MaybeHandle<Smi>(
Smi::FromEnum(TypedArraySetResultCodes::OVERLAPPING), isolate);
} else { // Non-overlapping typed arrays
return MaybeHandle<Smi>(
Smi::FromEnum(TypedArraySetResultCodes::NONOVERLAPPING), isolate);
}
return isolate->heap()->undefined_value();
}
} // anonymous namespace
} // namespace
// 22.2.3.23%TypedArray%.prototype.set ( overloaded [ , offset ] )
// 22.2.3.23 %TypedArray%.prototype.set ( overloaded [ , offset ] )
RUNTIME_FUNCTION(Runtime_TypedArraySet) {
HandleScope scope(isolate);
Handle<JSTypedArray> target = args.at<JSTypedArray>(0);
......@@ -362,69 +317,44 @@ RUNTIME_FUNCTION(Runtime_TypedArraySet) {
Handle<Smi> offset = args.at<Smi>(2);
DCHECK(!target->WasNeutered()); // Checked in TypedArrayPrototypeSet.
DCHECK(0 <= offset->value() && offset->value() <= Smi::kMaxValue);
DCHECK_LE(0, offset->value());
const uint32_t uint_offset = static_cast<uint32_t>(offset->value());
// TODO(cwhan.tunz): Implement CopyElements for overlapping cases, and use
// TypedArrayCopyElements for all case instead of this result code based
// branches
Handle<Smi> result_code;
if (obj->IsNumber()) {
// For number as a first argument, throw TypeError
// instead of silently ignoring the call, so that
// users know they did something wrong.
// (Consistent with Firefox and Blink/WebKit)
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidArgument));
} else if (obj->IsJSTypedArray()) {
// The non-overlapping case is handled in CSA.
Handle<JSTypedArray> source = Handle<JSTypedArray>::cast(obj);
return TypedArraySetFromOverlapping(isolate, target, source, uint_offset);
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj,
Object::ToObject(isolate, obj));
Handle<Object> len;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result_code,
TypedArraySetFastCases(isolate, target, obj, offset));
switch (static_cast<TypedArraySetResultCodes>(result_code->value())) {
case TypedArraySetResultCodes::OVERLAPPING: {
RETURN_FAILURE_ON_EXCEPTION(
isolate,
TypedArraySetFromOverlapping(
isolate, target, Handle<JSTypedArray>::cast(obj), uint_offset));
break;
}
case TypedArraySetResultCodes::NONOVERLAPPING: {
return TypedArrayCopyElements(
target, Handle<JSTypedArray>::cast(obj),
Handle<JSTypedArray>::cast(obj)->length_value(), uint_offset);
break;
}
case TypedArraySetResultCodes::NON_TYPED_ARRAY: {
if (obj->IsNumber()) {
// For number as a first argument, throw TypeError
// instead of silently ignoring the call, so that
// users know they did something wrong.
// (Consistent with Firefox and Blink/WebKit)
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidArgument));
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj,
Object::ToObject(isolate, obj));
Handle<Object> len;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, len,
Object::GetProperty(obj, isolate->factory()->length_string()));
if (len->IsUndefined(isolate)) {
break;
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len,
Object::ToLength(isolate, len));
DCHECK_GE(uint_offset, 0);
if (uint_offset + len->Number() > target->length_value()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
}
uint32_t int_l;
CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l));
return TypedArrayCopyElements(target, Handle<JSReceiver>::cast(obj),
int_l, uint_offset);
} break;
isolate, len,
Object::GetProperty(obj, isolate->factory()->length_string()));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len,
Object::ToLength(isolate, len));
if (uint_offset + len->Number() > target->length_value()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
}
return *isolate->factory()->undefined_value();
uint32_t int_l;
CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l));
Handle<JSReceiver> source = Handle<JSReceiver>::cast(obj);
ElementsAccessor* accessor = target->GetElementsAccessor();
return accessor->CopyElements(source, target, int_l, uint_offset);
}
} // namespace internal
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --mock-arraybuffer-allocator
(function TestBufferByteLengthNonSmi() {
const source_buffer_length = %_MaxSmi() + 1;
const source_buffer = new ArrayBuffer(source_buffer_length);
const source = new Uint16Array(source_buffer);
assertEquals(source_buffer_length, source_buffer.byteLength);
assertEquals(source_buffer_length / 2, source.length);
const target_buffer_length = %_MaxSmi() - 1;
const target_buffer = new ArrayBuffer(target_buffer_length);
const target = new Uint16Array(target_buffer);
assertEquals(target_buffer_length, target_buffer.byteLength);
assertEquals(target_buffer_length / 2, target.length);
assertThrows(() => target.set(source), RangeError);
})();
......@@ -639,28 +639,44 @@ function TestTypedArraySet() {
// Detached array buffer when converting offset.
{
const xs = new Int8Array(10);
let detached = false;
const offset = {
[Symbol.toPrimitive]() {
%ArrayBufferNeuter(xs.buffer);
detached = true;
return 0;
}
};
assertThrows(() => xs.set(xs, offset), TypeError);
assertEquals(true, detached);
for (const klass of typedArrayConstructors) {
const xs = new klass(10);
let detached = false;
const offset = {
[Symbol.toPrimitive]() {
%ArrayBufferNeuter(xs.buffer);
detached = true;
return 0;
}
};
assertThrows(() => xs.set(xs, offset), TypeError);
assertEquals(true, detached);
}
}
// Detached JSTypedArray source argument.
{
for (const klass of typedArrayConstructors) {
const a = new klass(2);
for (let i = 0; i < a.length; i++) a[i] = i;
%ArrayBufferNeuter(a.buffer);
const b = new klass(2);
assertThrows(() => b.set(a), TypeError);
}
}
// Various offset edge cases.
{
const xs = new Int8Array(10);
assertThrows(() => xs.set(xs, -1), RangeError);
assertThrows(() => xs.set(xs, -1 * 2**64), RangeError);
xs.set(xs, -0.0);
xs.set(xs, 0.0);
xs.set(xs, 0.5);
assertThrows(() => xs.set(xs, 2**64), RangeError);
for (const klass of typedArrayConstructors) {
const xs = new klass(10);
assertThrows(() => xs.set(xs, -1), RangeError);
assertThrows(() => xs.set(xs, -1 * 2**64), RangeError);
xs.set(xs, -0.0);
xs.set(xs, 0.0);
xs.set(xs, 0.5);
assertThrows(() => xs.set(xs, 2**64), RangeError);
}
}
// Exhaustively test elements kind combinations with JSArray source arg.
......
......@@ -104,7 +104,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/set/typedarray-arg-srcbuffer-detached-during-tointeger-offset-throws': [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],
......
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