Commit 7d60f78a authored by Franziska Hinkelmann's avatar Franziska Hinkelmann Committed by Commit Bot

[builtins] Move TypedArrayPrototypeSet to builtins

Bug: v8:6704
Change-Id: I77388b91061f934943a707a645080dfdcf481836
Reviewed-on: https://chromium-review.googlesource.com/645951Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Franziska Hinkelmann <franzih@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47756}
parent d5c1730a
......@@ -2904,6 +2904,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kTypedArrayPrototypeReduce, 1, false);
SimpleInstallFunction(prototype, "reduceRight",
Builtins::kTypedArrayPrototypeReduceRight, 1, false);
SimpleInstallFunction(prototype, "set", Builtins::kTypedArrayPrototypeSet,
1, false);
SimpleInstallFunction(prototype, "slice",
Builtins::kTypedArrayPrototypeSlice, 2, false);
SimpleInstallFunction(prototype, "some", Builtins::kTypedArrayPrototypeSome,
......
......@@ -987,6 +987,8 @@ namespace internal {
CPP(TypedArrayPrototypeLastIndexOf) \
/* ES6 #sec-%typedarray%.prototype.reverse */ \
CPP(TypedArrayPrototypeReverse) \
/* ES6 %TypedArray%.prototype.set */ \
CPP(TypedArrayPrototypeSet) \
/* ES6 #sec-%typedarray%.prototype.slice */ \
CPP(TypedArrayPrototypeSlice) \
/* ES6 %TypedArray%.prototype.every */ \
......
......@@ -277,6 +277,302 @@ BUILTIN(TypedArrayPrototypeReverse) {
return *array;
}
namespace {
Object* TypedArrayCopyElements(Handle<JSTypedArray> target,
Handle<JSReceiver> source, Object* length_obj) {
size_t length;
CHECK(TryNumberToSize(length_obj, &length));
ElementsAccessor* accessor = target->GetElementsAccessor();
return accessor->CopyElements(source, target, length);
}
enum class TypedArraySetResultCodes {
// Set from typed array of the same type.
// This is processed by TypedArraySetFastCases
SAME_TYPE,
// 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> TypedArraySetFromArrayLike(Isolate* isolate,
Handle<JSTypedArray> target,
Handle<Object> source,
int source_length, int offset) {
DCHECK_GE(source_length, 0);
DCHECK_GE(offset, 0);
for (int i = 0; i < source_length; i++) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::GetElement(isolate, source, i), Object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::SetElement(isolate, target, offset + i,
value, LanguageMode::STRICT),
Object);
}
return target;
}
MaybeHandle<Object> TypedArraySetFromOverlapping(Isolate* isolate,
Handle<JSTypedArray> target,
Handle<JSTypedArray> source,
int offset) {
DCHECK_GE(offset, 0);
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;
// Copy left part.
// First un-mutated byte after the next write
uint32_t target_ptr = 0;
CHECK(target->byte_offset()->ToUint32(&target_ptr));
target_ptr += (offset + 1) * targetElementSize;
// Next read at sourcePtr. We do not care for memory changing before
// sourcePtr - we have already copied it.
uint32_t source_ptr = 0;
CHECK(source->byte_offset()->ToUint32(&source_ptr));
uint32_t left_index;
for (left_index = 0; left_index < source_length && target_ptr <= source_ptr;
left_index++) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::GetElement(isolate, source, left_index),
Object);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
Object::SetElement(isolate, target, offset + left_index, value,
LanguageMode::STRICT),
Object);
target_ptr += targetElementSize;
source_ptr += sourceElementSize;
}
// Copy right part;
// First unmutated byte before the next write
CHECK(target->byte_offset()->ToUint32(&target_ptr));
target_ptr += (offset + source_length - 1) * targetElementSize;
// Next read before sourcePtr. We do not care for memory changing after
// sourcePtr - we have already copied it.
CHECK(target->byte_offset()->ToUint32(&source_ptr));
source_ptr += source_length * sourceElementSize;
uint32_t right_index;
DCHECK_GE(source_length, 1);
for (right_index = source_length - 1;
right_index > left_index && target_ptr >= source_ptr; right_index--) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::GetElement(isolate, source, right_index),
Object);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
Object::SetElement(isolate, target, offset + right_index, value,
LanguageMode::STRICT),
Object);
target_ptr -= targetElementSize;
source_ptr -= sourceElementSize;
}
std::vector<Handle<Object>> temp(right_index + 1 - left_index);
for (uint32_t i = left_index; i <= right_index; i++) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::GetElement(isolate, source, i), Object);
temp[i - left_index] = value;
}
for (uint32_t i = left_index; i <= right_index; i++) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
Object::SetElement(isolate, target, offset + i, temp[i - left_index],
LanguageMode::STRICT),
Object);
}
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);
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 the same type: use memmove.
if (target->type() == source->type()) {
memmove(target_base + offset * target->element_size(), source_base,
source_byte_length);
return MaybeHandle<Smi>(Smi::FromEnum(TypedArraySetResultCodes::SAME_TYPE),
isolate);
}
// 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);
}
}
} // anonymous namespace
// 22.2.3.23%TypedArray%.prototype.set ( overloaded [ , offset ] )
BUILTIN(TypedArrayPrototypeSet) {
HandleScope scope(isolate);
Handle<Object> target = args.receiver();
Handle<Object> obj = args.atOrUndefined(isolate, 1);
Handle<Object> offset = args.atOrUndefined(isolate, 2);
if (offset->IsUndefined(isolate)) {
offset = Handle<Object>(Smi::kZero, isolate);
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, offset,
Object::ToInteger(isolate, offset));
}
if (offset->Number() < 0) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kTypedArraySetNegativeOffset));
}
if (offset->Number() > Smi::kMaxValue) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
}
if (!target->IsJSTypedArray()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kNotTypedArray));
}
auto int_offset = static_cast<int>(offset->Number());
Handle<Smi> result_code;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result_code,
TypedArraySetFastCases(isolate, Handle<JSTypedArray>::cast(target), obj,
offset));
switch (static_cast<TypedArraySetResultCodes>(result_code->value())) {
case TypedArraySetResultCodes::SAME_TYPE: {
break;
}
case TypedArraySetResultCodes::OVERLAPPING: {
RETURN_FAILURE_ON_EXCEPTION(
isolate, TypedArraySetFromOverlapping(
isolate, Handle<JSTypedArray>::cast(target),
Handle<JSTypedArray>::cast(obj), int_offset));
break;
}
case TypedArraySetResultCodes::NONOVERLAPPING: {
if (int_offset == 0) {
TypedArrayCopyElements(Handle<JSTypedArray>::cast(target),
Handle<JSTypedArray>::cast(obj),
Handle<JSTypedArray>::cast(obj)->length());
} else {
RETURN_FAILURE_ON_EXCEPTION(
isolate,
TypedArraySetFromArrayLike(
isolate, Handle<JSTypedArray>::cast(target), obj,
Handle<JSTypedArray>::cast(obj)->length_value(), int_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));
uint32_t int_l;
CHECK(len->ToUint32(&int_l));
DCHECK_GE(int_offset, 0);
if (static_cast<uint32_t>(int_offset) + int_l >
Handle<JSTypedArray>::cast(target)->length_value()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
}
RETURN_FAILURE_ON_EXCEPTION(
isolate, TypedArraySetFromArrayLike(
isolate, Handle<JSTypedArray>::cast(target), obj, int_l,
int_offset));
} break;
}
return *isolate->factory()->undefined_value();
}
BUILTIN(TypedArrayPrototypeSlice) {
HandleScope scope(isolate);
......
......@@ -250,17 +250,6 @@ TYPED_ARRAYS(TYPED_ARRAY_SUBARRAY_CASE)
%SetForceInlineFlag(GlobalTypedArray.prototype.subarray);
// 22.2.3.23%TypedArray%.prototype.set ( overloaded [ , offset ] )
DEFINE_METHOD_LEN(
GlobalTypedArray.prototype,
set(obj, offset) {
// TODO(franzih): Migrate this to a proper builtin.
%_TypedArrayPrototypeSet(this, obj, offset);
},
1 /* Set function length. */
);
DEFINE_METHOD(
GlobalTypedArray.prototype,
get [toStringTagSymbol]() {
......
......@@ -100,295 +100,6 @@ RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
return *holder->GetBuffer();
}
namespace {
enum class TypedArraySetResultCodes {
// Set from typed array of the same type.
// This is processed by TypedArraySetFastCases
SAME_TYPE,
// 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> TypedArraySetFromArrayLike(Isolate* isolate,
Handle<JSTypedArray> target,
Handle<Object> source,
int source_length, int offset) {
DCHECK_GE(source_length, 0);
DCHECK_GE(offset, 0);
for (int i = 0; i < source_length; i++) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::GetElement(isolate, source, i), Object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::SetElement(isolate, target, offset + i,
value, LanguageMode::STRICT),
Object);
}
return target;
}
MaybeHandle<Object> TypedArraySetFromOverlapping(Isolate* isolate,
Handle<JSTypedArray> target,
Handle<JSTypedArray> source,
int offset) {
DCHECK_GE(offset, 0);
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;
// Copy left part.
// First un-mutated byte after the next write
uint32_t target_ptr = 0;
CHECK(target->byte_offset()->ToUint32(&target_ptr));
target_ptr += (offset + 1) * targetElementSize;
// Next read at sourcePtr. We do not care for memory changing before
// sourcePtr - we have already copied it.
uint32_t source_ptr = 0;
CHECK(source->byte_offset()->ToUint32(&source_ptr));
uint32_t left_index;
for (left_index = 0; left_index < source_length && target_ptr <= source_ptr;
left_index++) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::GetElement(isolate, source, left_index),
Object);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
Object::SetElement(isolate, target, offset + left_index, value,
LanguageMode::STRICT),
Object);
target_ptr += targetElementSize;
source_ptr += sourceElementSize;
}
// Copy right part;
// First unmutated byte before the next write
CHECK(target->byte_offset()->ToUint32(&target_ptr));
target_ptr += (offset + source_length - 1) * targetElementSize;
// Next read before sourcePtr. We do not care for memory changing after
// sourcePtr - we have already copied it.
CHECK(target->byte_offset()->ToUint32(&source_ptr));
source_ptr += source_length * sourceElementSize;
uint32_t right_index;
DCHECK_GE(source_length, 1);
for (right_index = source_length - 1;
right_index > left_index && target_ptr >= source_ptr; right_index--) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::GetElement(isolate, source, right_index),
Object);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
Object::SetElement(isolate, target, offset + right_index, value,
LanguageMode::STRICT),
Object);
target_ptr -= targetElementSize;
source_ptr -= sourceElementSize;
}
std::vector<Handle<Object>> temp(right_index + 1 - left_index);
for (uint32_t i = left_index; i <= right_index; i++) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Object::GetElement(isolate, source, i), Object);
temp[i - left_index] = value;
}
for (uint32_t i = left_index; i <= right_index; i++) {
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
Object::SetElement(isolate, target, offset + i, temp[i - left_index],
LanguageMode::STRICT),
Object);
}
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);
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 the same type: use memmove.
if (target->type() == source->type()) {
memmove(target_base + offset * target->element_size(), source_base,
source_byte_length);
return MaybeHandle<Smi>(Smi::FromEnum(TypedArraySetResultCodes::SAME_TYPE),
isolate);
}
// 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);
}
}
} // anonymous namespace
// 22.2.3.23%TypedArray%.prototype.set ( overloaded [ , offset ] )
RUNTIME_FUNCTION(Runtime_TypedArrayPrototypeSet) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, target, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, obj, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, offset, 2);
if (offset->IsUndefined(isolate)) {
offset = Handle<Object>(Smi::kZero, isolate);
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, offset,
Object::ToInteger(isolate, offset));
}
if (offset->Number() < 0) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kTypedArraySetNegativeOffset));
}
if (offset->Number() > Smi::kMaxValue) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
}
if (!target->IsJSTypedArray()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kNotTypedArray));
}
auto int_offset = static_cast<int>(offset->Number());
Handle<Smi> result_code;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result_code,
TypedArraySetFastCases(isolate, Handle<JSTypedArray>::cast(target), obj,
offset));
switch (static_cast<TypedArraySetResultCodes>(result_code->value())) {
case TypedArraySetResultCodes::SAME_TYPE: {
break;
}
case TypedArraySetResultCodes::OVERLAPPING: {
RETURN_FAILURE_ON_EXCEPTION(
isolate, TypedArraySetFromOverlapping(
isolate, Handle<JSTypedArray>::cast(target),
Handle<JSTypedArray>::cast(obj), int_offset));
break;
}
case TypedArraySetResultCodes::NONOVERLAPPING: {
if (int_offset == 0) {
TypedArrayCopyElements(Handle<JSTypedArray>::cast(target),
Handle<JSTypedArray>::cast(obj),
Handle<JSTypedArray>::cast(obj)->length());
} else {
RETURN_FAILURE_ON_EXCEPTION(
isolate,
TypedArraySetFromArrayLike(
isolate, Handle<JSTypedArray>::cast(target), obj,
Handle<JSTypedArray>::cast(obj)->length_value(), int_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));
uint32_t int_l;
CHECK(len->ToUint32(&int_l));
DCHECK_GE(int_offset, 0);
if (static_cast<uint32_t>(int_offset) + int_l >
Handle<JSTypedArray>::cast(target)->length_value()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
}
RETURN_FAILURE_ON_EXCEPTION(
isolate, TypedArraySetFromArrayLike(
isolate, Handle<JSTypedArray>::cast(target), obj, int_l,
int_offset));
} break;
default:
UNREACHABLE();
}
return *target;
}
namespace {
......
......@@ -623,7 +623,6 @@ namespace internal {
F(ArrayBufferViewWasNeutered, 1, 1) \
F(TypedArrayGetLength, 1, 1) \
F(TypedArrayGetBuffer, 1, 1) \
F(TypedArrayPrototypeSet, 3, 1) \
F(TypedArraySortFast, 1, 1) \
F(TypedArrayMaxSizeInHeap, 0, 1) \
F(IsTypedArray, 1, 1) \
......
......@@ -5,5 +5,5 @@
a.set([2], -1);
^
RangeError: Start offset is negative
at Uint8Array.set (native)
at Uint8Array.set (<anonymous>)
at *%(basename)s:6:3
\ No newline at end of file
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