Implement TypedArray.set function.

R=rossberg@chromium.org

Review URL: https://codereview.chromium.org/14581005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14576 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dslomov@chromium.org 2013-05-07 14:42:17 +00:00
parent e45abf08cc
commit b15bbfbe39
7 changed files with 258 additions and 1 deletions

View File

@ -104,12 +104,15 @@ var kMessages = {
parameterless_typed_array_constr:
["%0"," constructor should have at least one argument."],
not_typed_array: ["this is not a typed array."],
invalid_argument: ["invalid_argument"],
// RangeError
invalid_array_length: ["Invalid array length"],
invalid_array_buffer_length: ["Invalid array buffer length"],
invalid_typed_array_offset: ["Start offset is too large"],
invalid_typed_array_length: ["Length is too large"],
invalid_typed_array_alignment: ["%0", "of", "%1", "should be a multiple of", "%3"],
typed_array_set_source_too_large:
["Source is too large"],
stack_overflow: ["Maximum call stack size exceeded"],
invalid_time_value: ["Invalid time value"],
// SyntaxError

View File

@ -13431,6 +13431,33 @@ ExternalArrayType JSTypedArray::type() {
}
size_t JSTypedArray::element_size() {
switch (elements()->map()->instance_type()) {
case EXTERNAL_BYTE_ARRAY_TYPE:
return 1;
case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
return 1;
case EXTERNAL_SHORT_ARRAY_TYPE:
return 2;
case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
return 2;
case EXTERNAL_INT_ARRAY_TYPE:
return 4;
case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
return 4;
case EXTERNAL_FLOAT_ARRAY_TYPE:
return 4;
case EXTERNAL_DOUBLE_ARRAY_TYPE:
return 8;
case EXTERNAL_PIXEL_ARRAY_TYPE:
return 1;
default:
UNREACHABLE();
return 0;
}
}
Object* ExternalPixelArray::SetValue(uint32_t index, Object* value) {
uint8_t clamped_value = 0;
if (index < static_cast<uint32_t>(length())) {

View File

@ -8733,6 +8733,7 @@ class JSTypedArray: public JSObject {
static inline JSTypedArray* cast(Object* obj);
ExternalArrayType type();
size_t element_size();
// Dispatched behavior.
DECLARE_PRINTER(JSTypedArray)

View File

@ -891,6 +891,128 @@ TYPED_ARRAY_GETTER(Length, length)
#undef TYPED_ARRAY_GETTER
RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) {
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(Object, target_obj, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, source_obj, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, offset_obj, 2);
if (!target_obj->IsJSTypedArray())
return isolate->Throw(*isolate->factory()->NewTypeError(
"not_typed_array", HandleVector<Object>(NULL, 0)));
if (!source_obj->IsJSTypedArray())
return isolate->heap()->false_value();
Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj));
Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj));
size_t offset = NumberToSize(isolate, *offset_obj);
size_t target_length = NumberToSize(isolate, target->length());
size_t source_length = NumberToSize(isolate, source->length());
size_t target_byte_length = NumberToSize(isolate, target->byte_length());
size_t source_byte_length = NumberToSize(isolate, source->byte_length());
if (offset > target_length ||
offset + source_length > target_length ||
offset + source_length < offset) // overflow
return isolate->Throw(*isolate->factory()->NewRangeError(
"typed_array_set_source_too_large", HandleVector<Object>(NULL, 0)));
Handle<JSArrayBuffer> target_buffer(JSArrayBuffer::cast(target->buffer()));
Handle<JSArrayBuffer> source_buffer(JSArrayBuffer::cast(source->buffer()));
size_t target_offset = NumberToSize(isolate, target->byte_offset());
size_t source_offset = NumberToSize(isolate, source->byte_offset());
uint8_t* target_base =
static_cast<uint8_t*>(target_buffer->backing_store()) + target_offset;
uint8_t* source_base =
static_cast<uint8_t*>(source_buffer->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 isolate->heap()->true_value();
}
// 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)) {
size_t target_element_size = target->element_size();
size_t source_element_size = source->element_size();
size_t source_length = NumberToSize(isolate, source->length());
// Copy left part
size_t left_index;
{
// First un-mutated byte after the next write
uint8_t* target_ptr = target_base + (offset + 1) * target_element_size;
// Next read at source_ptr. We do not care for memory changing before
// source_ptr - we have already copied it.
uint8_t* source_ptr = source_base;
for (left_index = 0;
left_index < source_length && target_ptr <= source_ptr;
left_index++) {
Handle<Object> v = Object::GetElement(
source, static_cast<uint32_t>(left_index));
JSObject::SetElement(
target, static_cast<uint32_t>(offset + left_index), v,
NONE, kNonStrictMode);
target_ptr += target_element_size;
source_ptr += source_element_size;
}
}
// Copy right part
size_t right_index;
{
// First unmutated byte before the next write
uint8_t* target_ptr =
target_base + (offset + source_length - 1) * target_element_size;
// Next read before source_ptr. We do not care for memory changing after
// source_ptr - we have already copied it.
uint8_t* source_ptr =
source_base + source_length * source_element_size;
for (right_index = source_length - 1;
right_index >= left_index && target_ptr >= source_ptr;
right_index--) {
Handle<Object> v = Object::GetElement(
source, static_cast<uint32_t>(right_index));
JSObject::SetElement(
target, static_cast<uint32_t>(offset + right_index), v,
NONE, kNonStrictMode);
target_ptr -= target_element_size;
source_ptr -= source_element_size;
}
}
// There can be at most 8 entries left in the middle that need buffering
// (because the largest element_size is 8 times the smallest).
ASSERT((right_index + 1) - left_index <= 8);
Handle<Object> temp[8];
size_t idx;
for (idx = left_index; idx <= right_index; idx++) {
temp[idx - left_index] = Object::GetElement(
source, static_cast<uint32_t>(idx));
}
for (idx = left_index; idx <= right_index; idx++) {
JSObject::SetElement(
target, static_cast<uint32_t>(offset + idx), temp[idx-left_index],
NONE, kNonStrictMode);
}
} else { // Non-overlapping typed arrays
for (size_t idx = 0; idx < source_length; idx++) {
Handle<Object> value = Object::GetElement(
source, static_cast<uint32_t>(idx));
JSObject::SetElement(
target, static_cast<uint32_t>(offset + idx), value,
NONE, kNonStrictMode);
}
}
return isolate->heap()->true_value();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);

View File

@ -361,6 +361,7 @@ namespace internal {
F(TypedArrayGetByteLength, 1, 1) \
F(TypedArrayGetByteOffset, 1, 1) \
F(TypedArrayGetLength, 1, 1) \
F(TypedArraySetFastCases, 3, 1) \
\
/* Statements */ \
F(NewClosure, 3, 1) \

View File

@ -145,6 +145,22 @@ function CreateSubArray(elementSize, constructor) {
}
}
function TypedArraySet(obj, offset) {
var intOffset = IS_UNDEFINED(offset) ? 0 : TO_POSITIVE_INTEGER(offset);
if (%TypedArraySetFastCases(this, obj, intOffset))
return;
var l = obj.length;
if (IS_UNDEFINED(l)) {
throw MakeTypeError("invalid_argument");
}
if (intOffset + l > this.length) {
throw MakeRangeError("typed_array_set_source_too_large");
}
for (var i = 0; i < l; i++) {
this[intOffset + i] = obj[i];
}
}
// -------------------------------------------------------------------
@ -166,7 +182,8 @@ function SetupTypedArray(arrayId, name, constructor, elementSize) {
InstallGetter(constructor.prototype, "length", TypedArrayGetLength);
InstallFunctions(constructor.prototype, DONT_ENUM, $Array(
"subarray", CreateSubArray(elementSize, constructor)
"subarray", CreateSubArray(elementSize, constructor),
"set", TypedArraySet
));
}

View File

@ -371,6 +371,92 @@ for(i = 0; i < typedArrayConstructors.lenght; i++) {
}
function TestTypedArraySet() {
// Test array.set in different combinations.
function assertArrayPrefix(expected, array) {
for (var i = 0; i < expected.length; ++i) {
assertEquals(expected[i], array[i]);
}
}
var a11 = new Int16Array([1, 2, 3, 4, 0, -1])
var a12 = new Uint16Array(15)
a12.set(a11, 3)
assertArrayPrefix([0, 0, 0, 1, 2, 3, 4, 0, 0xffff, 0, 0], a12)
assertThrows(function(){ a11.set(a12) })
var a21 = [1, undefined, 10, NaN, 0, -1, {valueOf: function() {return 3}}]
var a22 = new Int32Array(12)
a22.set(a21, 2)
assertArrayPrefix([0, 0, 1, 0, 10, 0, 0, -1, 3, 0], a22)
var a31 = new Float32Array([2, 4, 6, 8, 11, NaN, 1/0, -3])
var a32 = a31.subarray(2, 6)
a31.set(a32, 4)
assertArrayPrefix([2, 4, 6, 8, 6, 8, 11, NaN], a31)
assertArrayPrefix([6, 8, 6, 8], a32)
var a4 = new Uint8ClampedArray([3,2,5,6])
a4.set(a4)
assertArrayPrefix([3, 2, 5, 6], a4)
// Cases with overlapping backing store but different element sizes.
var b = new ArrayBuffer(4)
var a5 = new Int16Array(b)
var a50 = new Int8Array(b)
var a51 = new Int8Array(b, 0, 2)
var a52 = new Int8Array(b, 1, 2)
var a53 = new Int8Array(b, 2, 2)
a5.set([0x5050, 0x0a0a])
assertArrayPrefix([0x50, 0x50, 0x0a, 0x0a], a50)
assertArrayPrefix([0x50, 0x50], a51)
assertArrayPrefix([0x50, 0x0a], a52)
assertArrayPrefix([0x0a, 0x0a], a53)
a50.set([0x50, 0x50, 0x0a, 0x0a])
a51.set(a5)
assertArrayPrefix([0x50, 0x0a, 0x0a, 0x0a], a50)
a50.set([0x50, 0x50, 0x0a, 0x0a])
a52.set(a5)
assertArrayPrefix([0x50, 0x50, 0x0a, 0x0a], a50)
a50.set([0x50, 0x50, 0x0a, 0x0a])
a53.set(a5)
assertArrayPrefix([0x50, 0x50, 0x50, 0x0a], a50)
a50.set([0x50, 0x51, 0x0a, 0x0b])
a5.set(a51)
assertArrayPrefix([0x0050, 0x0051], a5)
a50.set([0x50, 0x51, 0x0a, 0x0b])
a5.set(a52)
assertArrayPrefix([0x0051, 0x000a], a5)
a50.set([0x50, 0x51, 0x0a, 0x0b])
a5.set(a53)
assertArrayPrefix([0x000a, 0x000b], a5)
// Mixed types of same size.
var a61 = new Float32Array([1.2, 12.3])
var a62 = new Int32Array(2)
a62.set(a61)
assertArrayPrefix([1, 12], a62)
a61.set(a62)
assertArrayPrefix([1, 12], a61)
// Invalid source
var a = new Uint16Array(50);
assertThrows(function() { a.set(0) }, TypeError);
assertThrows(function() { a.set({}) }, TypeError);
assertThrows(function() { a.set.call({}) }, TypeError);
assertThrows(function() { a.set.call([]) }, TypeError);
}
TestTypedArraySet();
// General tests for properties
// Test property attribute [[Enumerable]]