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:
parent
e45abf08cc
commit
b15bbfbe39
@ -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
|
||||
|
@ -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())) {
|
||||
|
@ -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)
|
||||
|
122
src/runtime.cc
122
src/runtime.cc
@ -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);
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -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]]
|
||||
|
Loading…
Reference in New Issue
Block a user