mirror of https://github.com/lukechilds/node.git
Ben Noordhuis
14 years ago
4 changed files with 800 additions and 0 deletions
@ -0,0 +1,762 @@ |
|||||
|
// V8 Typed Array implementation.
|
||||
|
// (c) Dean McNamee <dean@gmail.com>, 2011.
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
// of this software and associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including without limitation the
|
||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be included in
|
||||
|
// all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
|
// IN THE SOFTWARE.
|
||||
|
|
||||
|
#include <stdlib.h> // calloc, etc |
||||
|
#include <string.h> // memmove |
||||
|
|
||||
|
#include <v8.h> |
||||
|
|
||||
|
#include "v8_typed_array.h" |
||||
|
|
||||
|
namespace { |
||||
|
|
||||
|
v8::Handle<v8::Value> ThrowError(const char* msg) { |
||||
|
return v8::ThrowException(v8::Exception::Error(v8::String::New(msg))); |
||||
|
} |
||||
|
|
||||
|
v8::Handle<v8::Value> ThrowTypeError(const char* msg) { |
||||
|
return v8::ThrowException(v8::Exception::TypeError(v8::String::New(msg))); |
||||
|
} |
||||
|
|
||||
|
v8::Handle<v8::Value> ThrowRangeError(const char* msg) { |
||||
|
return v8::ThrowException(v8::Exception::RangeError(v8::String::New(msg))); |
||||
|
} |
||||
|
|
||||
|
struct BatchedMethods { |
||||
|
const char* name; |
||||
|
v8::Handle<v8::Value> (*func)(const v8::Arguments& args); |
||||
|
}; |
||||
|
|
||||
|
class ArrayBuffer { |
||||
|
public: |
||||
|
static v8::Persistent<v8::FunctionTemplate> GetTemplate() { |
||||
|
static v8::Persistent<v8::FunctionTemplate> ft_cache; |
||||
|
if (!ft_cache.IsEmpty()) |
||||
|
return ft_cache; |
||||
|
|
||||
|
v8::HandleScope scope; |
||||
|
ft_cache = v8::Persistent<v8::FunctionTemplate>::New( |
||||
|
v8::FunctionTemplate::New(&ArrayBuffer::V8New)); |
||||
|
ft_cache->SetClassName(v8::String::New("ArrayBuffer")); |
||||
|
v8::Local<v8::ObjectTemplate> instance = ft_cache->InstanceTemplate(); |
||||
|
instance->SetInternalFieldCount(1); // Buffer.
|
||||
|
|
||||
|
return ft_cache; |
||||
|
} |
||||
|
|
||||
|
static bool HasInstance(v8::Handle<v8::Value> value) { |
||||
|
return GetTemplate()->HasInstance(value); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
static void WeakCallback(v8::Persistent<v8::Value> value, void* data) { |
||||
|
v8::Object* obj = v8::Object::Cast(*value); |
||||
|
|
||||
|
void* ptr = obj->GetIndexedPropertiesExternalArrayData(); |
||||
|
int element_size = v8_typed_array::SizeOfArrayElementForType( |
||||
|
obj->GetIndexedPropertiesExternalArrayDataType()); |
||||
|
int size = |
||||
|
obj->GetIndexedPropertiesExternalArrayDataLength() * element_size; |
||||
|
|
||||
|
v8::V8::AdjustAmountOfExternalAllocatedMemory(-size); |
||||
|
|
||||
|
value.ClearWeak(); |
||||
|
value.Dispose(); |
||||
|
|
||||
|
free(ptr); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> V8New(const v8::Arguments& args) { |
||||
|
if (!args.IsConstructCall()) |
||||
|
return ThrowTypeError("Constructor cannot be called as a function."); |
||||
|
|
||||
|
// To match Chrome, we allow "new ArrayBuffer()".
|
||||
|
// if (args.Length() != 1)
|
||||
|
// return ThrowError("Wrong number of arguments.");
|
||||
|
|
||||
|
if (args[0]->Int32Value() < 0) { |
||||
|
return ThrowRangeError("ArrayBufferView size is not a small enough " |
||||
|
"positive integer."); |
||||
|
} |
||||
|
|
||||
|
size_t num_bytes = args[0]->Uint32Value(); |
||||
|
void* buf = calloc(num_bytes, 1); |
||||
|
if (!buf) |
||||
|
return ThrowError("Unable to allocate ArrayBuffer."); |
||||
|
|
||||
|
args.This()->SetPointerInInternalField(0, buf); |
||||
|
|
||||
|
args.This()->Set(v8::String::New("byteLength"), |
||||
|
v8::Integer::NewFromUnsigned(num_bytes), |
||||
|
(v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete)); |
||||
|
|
||||
|
// NOTE(deanm): This is not in the spec, you shouldn't be able to index
|
||||
|
// the ArrayBuffer. However, it currently simplifies some handling in our
|
||||
|
// implementation, so we make ArrayView operator[] act like an Uint8Array.
|
||||
|
// , This allows DataView to work with both ArrayBuffers and TypedArrays.
|
||||
|
args.This()->SetIndexedPropertiesToExternalArrayData( |
||||
|
buf, v8::kExternalUnsignedByteArray, num_bytes); |
||||
|
|
||||
|
v8::V8::AdjustAmountOfExternalAllocatedMemory(num_bytes); |
||||
|
|
||||
|
v8::Persistent<v8::Object> persistent = |
||||
|
v8::Persistent<v8::Object>::New(args.This()); |
||||
|
persistent.MakeWeak(NULL, &ArrayBuffer::WeakCallback); |
||||
|
|
||||
|
return args.This(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
static bool checkAlignment(unsigned int val, unsigned int bytes) { |
||||
|
return (val & (bytes - 1)) == 0; // Handles bytes == 0.
|
||||
|
} |
||||
|
|
||||
|
template <unsigned int TBytes, v8::ExternalArrayType TEAType> |
||||
|
class TypedArray { |
||||
|
public: |
||||
|
static v8::Persistent<v8::FunctionTemplate> GetTemplate() { |
||||
|
static v8::Persistent<v8::FunctionTemplate> ft_cache; |
||||
|
if (!ft_cache.IsEmpty()) |
||||
|
return ft_cache; |
||||
|
|
||||
|
v8::HandleScope scope; |
||||
|
ft_cache = v8::Persistent<v8::FunctionTemplate>::New( |
||||
|
v8::FunctionTemplate::New(&TypedArray<TBytes, TEAType>::V8New)); |
||||
|
v8::Local<v8::ObjectTemplate> instance = ft_cache->InstanceTemplate(); |
||||
|
instance->SetInternalFieldCount(0); |
||||
|
|
||||
|
ft_cache->Set(v8::String::New("BYTES_PER_ELEMENT"), |
||||
|
v8::Uint32::New(TBytes), v8::ReadOnly); |
||||
|
instance->Set(v8::String::New("BYTES_PER_ELEMENT"), |
||||
|
v8::Uint32::New(TBytes), v8::ReadOnly); |
||||
|
|
||||
|
v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache); |
||||
|
|
||||
|
static BatchedMethods methods[] = { |
||||
|
{ "set", &TypedArray<TBytes, TEAType>::set }, |
||||
|
{ "subarray", &TypedArray<TBytes, TEAType>::subarray }, |
||||
|
}; |
||||
|
|
||||
|
for (size_t i = 0; i < sizeof(methods) / sizeof(*methods); ++i) { |
||||
|
instance->Set(v8::String::New(methods[i].name), |
||||
|
v8::FunctionTemplate::New(methods[i].func, |
||||
|
v8::Handle<v8::Value>(), |
||||
|
default_signature)); |
||||
|
} |
||||
|
|
||||
|
return ft_cache; |
||||
|
} |
||||
|
|
||||
|
static bool HasInstance(v8::Handle<v8::Value> value) { |
||||
|
return GetTemplate()->HasInstance(value); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
static v8::Handle<v8::Value> V8New(const v8::Arguments& args) { |
||||
|
if (!args.IsConstructCall()) |
||||
|
return ThrowTypeError("Constructor cannot be called as a function."); |
||||
|
|
||||
|
// To match Chrome, we allow "new Float32Array()".
|
||||
|
// if (args.Length() != 1)
|
||||
|
// return ThrowError("Wrong number of arguments.");
|
||||
|
|
||||
|
v8::Local<v8::Object> buffer; |
||||
|
unsigned int length = 0; |
||||
|
unsigned int byte_offset = 0; |
||||
|
|
||||
|
if (ArrayBuffer::HasInstance(args[0])) { // ArrayBuffer constructor.
|
||||
|
buffer = v8::Local<v8::Object>::Cast(args[0]); |
||||
|
unsigned int buflen = |
||||
|
buffer->GetIndexedPropertiesExternalArrayDataLength(); |
||||
|
|
||||
|
if (args[1]->Int32Value() < 0) |
||||
|
return ThrowRangeError("Byte offset out of range."); |
||||
|
byte_offset = args[1]->Uint32Value(); |
||||
|
|
||||
|
if (!checkAlignment(byte_offset, TBytes)) |
||||
|
return ThrowRangeError("Byte offset is not aligned."); |
||||
|
|
||||
|
if (args.Length() > 2) { |
||||
|
if (args[2]->Int32Value() < 0) |
||||
|
return ThrowRangeError("Length out of range."); |
||||
|
length = args[2]->Uint32Value(); |
||||
|
} else { |
||||
|
if (buflen < byte_offset || |
||||
|
!checkAlignment(buflen - byte_offset, TBytes)) { |
||||
|
return ThrowRangeError("Byte offset / length is not aligned."); |
||||
|
} |
||||
|
length = (buflen - byte_offset) / TBytes; |
||||
|
} |
||||
|
|
||||
|
// NOTE(deanm): Sloppy integer overflow checks.
|
||||
|
if (byte_offset > buflen || byte_offset + length > buflen || |
||||
|
byte_offset + length * TBytes > buflen) { |
||||
|
return ThrowRangeError("Length is out of range."); |
||||
|
} |
||||
|
|
||||
|
// TODO(deanm): Error check.
|
||||
|
void* buf = buffer->GetPointerFromInternalField(0); |
||||
|
args.This()->SetIndexedPropertiesToExternalArrayData( |
||||
|
reinterpret_cast<char*>(buf) + byte_offset, TEAType, length); |
||||
|
} else if (args[0]->IsObject()) { // TypedArray / type[] constructor.
|
||||
|
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(args[0]); |
||||
|
length = obj->Get(v8::String::New("length"))->Uint32Value(); |
||||
|
|
||||
|
// TODO(deanm): Handle integer overflow.
|
||||
|
v8::Handle<v8::Value> argv[1] = { |
||||
|
v8::Integer::NewFromUnsigned(length * TBytes)}; |
||||
|
buffer = ArrayBuffer::GetTemplate()-> |
||||
|
GetFunction()->NewInstance(1, argv); |
||||
|
|
||||
|
void* buf = buffer->GetPointerFromInternalField(0); |
||||
|
args.This()->SetIndexedPropertiesToExternalArrayData( |
||||
|
buf, TEAType, length); |
||||
|
// TODO(deanm): check for failure.
|
||||
|
for (uint32_t i = 0; i < length; ++i) { |
||||
|
// Use the v8 setter to deal with typing. Maybe slow?
|
||||
|
args.This()->Set(i, obj->Get(i)); |
||||
|
} |
||||
|
} else { // length constructor.
|
||||
|
// Try to match Chrome, Float32Array(""), Float32Array(true/false) is
|
||||
|
// okay, but Float32Array(null) throws a TypeError and
|
||||
|
// Float32Array(undefined) throw a RangeError.
|
||||
|
if (args.Length() > 0 && (args[0]->IsUndefined() || args[0]->IsNull())) |
||||
|
return ThrowTypeError("Type error"); |
||||
|
|
||||
|
if (args[0]->Int32Value() < 0) { |
||||
|
return ThrowRangeError("ArrayBufferView size is not a small enough " |
||||
|
"positive integer."); |
||||
|
} |
||||
|
|
||||
|
length = args[0]->Uint32Value(); |
||||
|
// TODO(deanm): Handle integer overflow.
|
||||
|
v8::Handle<v8::Value> argv[1] = { |
||||
|
v8::Integer::NewFromUnsigned(length * TBytes)}; |
||||
|
|
||||
|
buffer = ArrayBuffer::GetTemplate()-> |
||||
|
GetFunction()->NewInstance(1, argv); |
||||
|
void* buf = buffer->GetPointerFromInternalField(0); |
||||
|
|
||||
|
args.This()->SetIndexedPropertiesToExternalArrayData( |
||||
|
buf, TEAType, length); |
||||
|
// TODO(deanm): check for failure.
|
||||
|
} |
||||
|
|
||||
|
args.This()->Set(v8::String::New("buffer"), |
||||
|
buffer, |
||||
|
(v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete)); |
||||
|
args.This()->Set(v8::String::New("length"), |
||||
|
v8::Integer::NewFromUnsigned(length), |
||||
|
(v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete)); |
||||
|
args.This()->Set(v8::String::New("byteOffset"), |
||||
|
v8::Integer::NewFromUnsigned(byte_offset), |
||||
|
(v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete)); |
||||
|
args.This()->Set(v8::String::New("byteLength"), |
||||
|
v8::Integer::NewFromUnsigned(length * TBytes), |
||||
|
(v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete)); |
||||
|
|
||||
|
return args.This(); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> set(const v8::Arguments& args) { |
||||
|
if (args.Length() < 1) |
||||
|
return ThrowError("Wrong number of arguments."); |
||||
|
|
||||
|
if (!args[0]->IsObject()) |
||||
|
return ThrowTypeError("Type error."); |
||||
|
|
||||
|
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(args[0]); |
||||
|
|
||||
|
if (TypedArray<TBytes, TEAType>::HasInstance(obj)) { // ArrayBufferView.
|
||||
|
v8::Handle<v8::Object> src_buffer = v8::Handle<v8::Object>::Cast( |
||||
|
obj->Get(v8::String::New("buffer"))); |
||||
|
v8::Handle<v8::Object> dst_buffer = v8::Handle<v8::Object>::Cast( |
||||
|
args.This()->Get(v8::String::New("buffer"))); |
||||
|
|
||||
|
if (args[1]->Int32Value() < 0) |
||||
|
return ThrowRangeError("Offset may not be negative."); |
||||
|
|
||||
|
unsigned int offset = args[1]->Uint32Value(); |
||||
|
unsigned int src_length = |
||||
|
obj->Get(v8::String::New("length"))->Uint32Value(); |
||||
|
unsigned int dst_length = |
||||
|
args.This()->Get(v8::String::New("length"))->Uint32Value(); |
||||
|
if (offset > dst_length) |
||||
|
return ThrowRangeError("Offset out of range."); |
||||
|
|
||||
|
if (src_length > dst_length - offset) |
||||
|
return ThrowRangeError("Offset/length out of range."); |
||||
|
|
||||
|
// We don't want to get the buffer pointer, because that means we'll have
|
||||
|
// to just do the calculations for byteOffset / byteLength again.
|
||||
|
// Instead just use the pointer on the external array data.
|
||||
|
void* src_ptr = obj->GetIndexedPropertiesExternalArrayData(); |
||||
|
void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData(); |
||||
|
|
||||
|
// From the spec:
|
||||
|
// If the input array is a TypedArray, the two arrays may use the same
|
||||
|
// underlying ArrayBuffer. In this situation, setting the values takes
|
||||
|
// place as if all the data is first copied into a temporary buffer that
|
||||
|
// does not overlap either of the arrays, and then the data from the
|
||||
|
// temporary buffer is copied into the current array.
|
||||
|
memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes, |
||||
|
src_ptr, src_length * TBytes); |
||||
|
} else { // type[]
|
||||
|
if (args[1]->Int32Value() < 0) |
||||
|
return ThrowRangeError("Offset may not be negative."); |
||||
|
|
||||
|
unsigned int src_length = |
||||
|
obj->Get(v8::String::New("length"))->Uint32Value(); |
||||
|
unsigned int dst_length = |
||||
|
args.This()->Get(v8::String::New("length"))->Uint32Value(); |
||||
|
unsigned int offset = args[1]->Uint32Value(); |
||||
|
|
||||
|
if (offset > dst_length) |
||||
|
return ThrowRangeError("Offset out of range."); |
||||
|
|
||||
|
if (src_length > dst_length - offset) |
||||
|
return ThrowRangeError("Offset/length out of range."); |
||||
|
|
||||
|
for (uint32_t i = 0; i < src_length; ++i) { |
||||
|
// Use the v8 setter to deal with typing. Maybe slow?
|
||||
|
args.This()->Set(i + offset, obj->Get(i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return v8::Undefined(); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> subarray(const v8::Arguments& args) { |
||||
|
// TODO(deanm): The unsigned / signed type mixing makes me super nervous.
|
||||
|
|
||||
|
unsigned int length = |
||||
|
args.This()->Get(v8::String::New("length"))->Uint32Value(); |
||||
|
int begin = args[0]->Int32Value(); |
||||
|
int end = length; |
||||
|
if (args.Length() > 1) |
||||
|
end = args[1]->Int32Value(); |
||||
|
|
||||
|
if (begin < 0) begin = length + begin; |
||||
|
if (begin < 0) begin = 0; |
||||
|
if (begin > length) begin = length; |
||||
|
|
||||
|
if (end < 0) end = length + end; |
||||
|
if (end < 0) end = 0; |
||||
|
if (end > length) end = length; |
||||
|
|
||||
|
if (begin > end) begin = end; |
||||
|
|
||||
|
int byte_offset = begin * TBytes + |
||||
|
args.This()->Get(v8::String::New("byteOffset"))->Uint32Value(); |
||||
|
|
||||
|
// Call through to the ArrayBuffer, byteOffset, length constructor.
|
||||
|
v8::Handle<v8::Value> argv[] = { |
||||
|
args.This()->Get(v8::String::New("buffer")), |
||||
|
v8::Integer::New(byte_offset), |
||||
|
v8::Integer::New(end - begin)}; |
||||
|
return TypedArray<TBytes, TEAType>::GetTemplate()-> |
||||
|
GetFunction()->NewInstance(3, argv); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
class Int8Array : public TypedArray<1, v8::kExternalByteArray> { }; |
||||
|
class Uint8Array : public TypedArray<1, v8::kExternalUnsignedByteArray> { }; |
||||
|
class Int16Array : public TypedArray<2, v8::kExternalShortArray> { }; |
||||
|
class Uint16Array : public TypedArray<2, v8::kExternalUnsignedShortArray> { }; |
||||
|
class Int32Array : public TypedArray<4, v8::kExternalIntArray> { }; |
||||
|
class Uint32Array : public TypedArray<4, v8::kExternalUnsignedIntArray> { }; |
||||
|
class Float32Array : public TypedArray<4, v8::kExternalFloatArray> { }; |
||||
|
|
||||
|
template <typename T> |
||||
|
v8::Handle<v8::Value> cTypeToValue(T) { |
||||
|
return v8::Undefined(); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
v8::Handle<v8::Value> cTypeToValue(unsigned char val) { |
||||
|
return v8::Integer::NewFromUnsigned(val); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
v8::Handle<v8::Value> cTypeToValue(char val) { |
||||
|
return v8::Integer::New(val); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
v8::Handle<v8::Value> cTypeToValue(unsigned short val) { |
||||
|
return v8::Integer::NewFromUnsigned(val); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
v8::Handle<v8::Value> cTypeToValue(short val) { |
||||
|
return v8::Integer::New(val); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
v8::Handle<v8::Value> cTypeToValue(unsigned int val) { |
||||
|
return v8::Integer::NewFromUnsigned(val); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
v8::Handle<v8::Value> cTypeToValue(int val) { |
||||
|
return v8::Integer::New(val); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
v8::Handle<v8::Value> cTypeToValue(float val) { |
||||
|
return v8::Number::New(val); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
v8::Handle<v8::Value> cTypeToValue(double val) { |
||||
|
return v8::Number::New(val); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
template <typename T> |
||||
|
T valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
unsigned char valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return value->Uint32Value(); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
char valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return value->Int32Value(); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
unsigned short valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return value->Uint32Value(); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
short valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return value->Int32Value(); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
unsigned int valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return value->Uint32Value(); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
int valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return value->Int32Value(); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
float valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return value->NumberValue(); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
double valueToCType(v8::Handle<v8::Value> value) { |
||||
|
return value->NumberValue(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
class DataView { |
||||
|
public: |
||||
|
static v8::Persistent<v8::FunctionTemplate> GetTemplate() { |
||||
|
static v8::Persistent<v8::FunctionTemplate> ft_cache; |
||||
|
if (!ft_cache.IsEmpty()) |
||||
|
return ft_cache; |
||||
|
|
||||
|
v8::HandleScope scope; |
||||
|
ft_cache = v8::Persistent<v8::FunctionTemplate>::New( |
||||
|
v8::FunctionTemplate::New(&DataView::V8New)); |
||||
|
ft_cache->SetClassName(v8::String::New("DataView")); |
||||
|
v8::Local<v8::ObjectTemplate> instance = ft_cache->InstanceTemplate(); |
||||
|
instance->SetInternalFieldCount(0); |
||||
|
|
||||
|
v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache); |
||||
|
|
||||
|
static BatchedMethods methods[] = { |
||||
|
{ "getUint8", &DataView::getUint8 }, |
||||
|
{ "getInt8", &DataView::getInt8 }, |
||||
|
{ "getUint16", &DataView::getUint16 }, |
||||
|
{ "getInt16", &DataView::getInt16 }, |
||||
|
{ "getUint32", &DataView::getUint32 }, |
||||
|
{ "getInt32", &DataView::getInt32 }, |
||||
|
{ "getFloat32", &DataView::getFloat32 }, |
||||
|
{ "getFloat64", &DataView::getFloat64 }, |
||||
|
{ "setUint8", &DataView::setUint8 }, |
||||
|
{ "setInt8", &DataView::setInt8 }, |
||||
|
{ "setUint16", &DataView::setUint16 }, |
||||
|
{ "setInt16", &DataView::setInt16 }, |
||||
|
{ "setUint32", &DataView::setUint32 }, |
||||
|
{ "setInt32", &DataView::setInt32 }, |
||||
|
{ "setFloat32", &DataView::setFloat32 }, |
||||
|
{ "setFloat64", &DataView::setFloat64 }, |
||||
|
}; |
||||
|
|
||||
|
for (size_t i = 0; i < sizeof(methods) / sizeof(*methods); ++i) { |
||||
|
instance->Set(v8::String::New(methods[i].name), |
||||
|
v8::FunctionTemplate::New(methods[i].func, |
||||
|
v8::Handle<v8::Value>(), |
||||
|
default_signature)); |
||||
|
} |
||||
|
|
||||
|
return ft_cache; |
||||
|
} |
||||
|
|
||||
|
static bool HasInstance(v8::Handle<v8::Value> value) { |
||||
|
return GetTemplate()->HasInstance(value); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
static v8::Handle<v8::Value> V8New(const v8::Arguments& args) { |
||||
|
if (!args.IsConstructCall()) |
||||
|
return ThrowTypeError("Constructor cannot be called as a function."); |
||||
|
|
||||
|
if (args.Length() < 1) |
||||
|
return ThrowError("Wrong number of arguments."); |
||||
|
|
||||
|
if (!args[0]->IsObject()) |
||||
|
return ThrowError("Object must be an ArrayBuffer."); |
||||
|
|
||||
|
v8::Handle<v8::Object> buffer = v8::Handle<v8::Object>::Cast(args[0]); |
||||
|
if (!buffer->HasIndexedPropertiesInExternalArrayData()) |
||||
|
return ThrowError("Object must be an ArrayBuffer."); |
||||
|
|
||||
|
unsigned int byte_length = |
||||
|
buffer->GetIndexedPropertiesExternalArrayDataLength(); |
||||
|
unsigned int byte_offset = args[1]->Uint32Value(); |
||||
|
|
||||
|
if (args[1]->Int32Value() < 0 || byte_offset >= byte_length) |
||||
|
return ThrowRangeError("byteOffset out of range."); |
||||
|
|
||||
|
if (!args[2]->IsUndefined()) { |
||||
|
if (args[2]->Int32Value() < 0) |
||||
|
return ThrowRangeError("byteLength out of range."); |
||||
|
unsigned int new_byte_length = args[2]->Uint32Value(); |
||||
|
if (new_byte_length > byte_length) |
||||
|
return ThrowRangeError("byteLength out of range."); |
||||
|
if (byte_offset + new_byte_length > byte_length) |
||||
|
return ThrowRangeError("byteOffset/byteLength out of range."); |
||||
|
byte_length = new_byte_length; |
||||
|
} else { |
||||
|
// Adjust the original byte_length from total length to length to end.
|
||||
|
byte_length -= byte_offset; |
||||
|
} |
||||
|
|
||||
|
void* buf = buffer->GetIndexedPropertiesExternalArrayData(); |
||||
|
|
||||
|
// Like ArrayBuffer, we violate the spec and add an operator[].
|
||||
|
args.This()->SetIndexedPropertiesToExternalArrayData( |
||||
|
reinterpret_cast<char*>(buf) + byte_offset, |
||||
|
v8::kExternalUnsignedByteArray, byte_length); |
||||
|
|
||||
|
args.This()->Set(v8::String::New("buffer"), |
||||
|
buffer, |
||||
|
(v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete)); |
||||
|
args.This()->Set(v8::String::New("byteOffset"), |
||||
|
v8::Integer::NewFromUnsigned(byte_offset), |
||||
|
(v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete)); |
||||
|
args.This()->Set(v8::String::New("byteLength"), |
||||
|
v8::Integer::NewFromUnsigned(byte_length), |
||||
|
(v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete)); |
||||
|
return args.This(); |
||||
|
} |
||||
|
|
||||
|
// TODO(deanm): This isn't beautiful or optimal.
|
||||
|
static void swizzle(char* buf, size_t len) { |
||||
|
for (int i = 0; i < len / 2; ++i) { |
||||
|
char t = buf[i]; |
||||
|
buf[i] = buf[len - i - 1]; |
||||
|
buf[len - i - 1] = t; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <typename T> |
||||
|
static T getValue(void* ptr, unsigned int index, bool swiz) { |
||||
|
char buf[sizeof(T)]; |
||||
|
memcpy(buf, reinterpret_cast<char*>(ptr) + index, sizeof(T)); |
||||
|
if (swiz) |
||||
|
swizzle(buf, sizeof(T)); |
||||
|
T val; |
||||
|
memcpy(&val, buf, sizeof(T)); |
||||
|
return val; |
||||
|
} |
||||
|
|
||||
|
template <typename T> |
||||
|
static void setValue(void* ptr, unsigned int index, T val, bool swiz) { |
||||
|
char buf[sizeof(T)]; |
||||
|
memcpy(buf, &val, sizeof(T)); |
||||
|
if (swiz) |
||||
|
swizzle(buf, sizeof(T)); |
||||
|
memcpy(reinterpret_cast<char*>(ptr) + index, buf, sizeof(T)); |
||||
|
} |
||||
|
|
||||
|
template <typename T> |
||||
|
static v8::Handle<v8::Value> getGeneric(const v8::Arguments& args) { |
||||
|
if (args.Length() < 1) |
||||
|
return ThrowError("Wrong number of arguments."); |
||||
|
|
||||
|
unsigned int index = args[0]->Uint32Value(); |
||||
|
bool little_endian = args[1]->BooleanValue(); |
||||
|
// TODO(deanm): All of these things should be cacheable.
|
||||
|
int element_size = v8_typed_array::SizeOfArrayElementForType( |
||||
|
args.This()->GetIndexedPropertiesExternalArrayDataType()); |
||||
|
int size = args.This()->GetIndexedPropertiesExternalArrayDataLength() * |
||||
|
element_size; |
||||
|
|
||||
|
if (index + sizeof(T) > size) // TODO(deanm): integer overflow.
|
||||
|
return ThrowError("Index out of range."); |
||||
|
|
||||
|
void* ptr = args.This()->GetIndexedPropertiesExternalArrayData(); |
||||
|
return cTypeToValue<T>(getValue<T>(ptr, index, !little_endian)); |
||||
|
} |
||||
|
|
||||
|
template <typename T> |
||||
|
static v8::Handle<v8::Value> setGeneric(const v8::Arguments& args) { |
||||
|
if (args.Length() < 2) |
||||
|
return ThrowError("Wrong number of arguments."); |
||||
|
|
||||
|
unsigned int index = args[0]->Int32Value(); |
||||
|
bool little_endian = args[2]->BooleanValue(); |
||||
|
// TODO(deanm): All of these things should be cacheable.
|
||||
|
int element_size = v8_typed_array::SizeOfArrayElementForType( |
||||
|
args.This()->GetIndexedPropertiesExternalArrayDataType()); |
||||
|
int size = args.This()->GetIndexedPropertiesExternalArrayDataLength() * |
||||
|
element_size; |
||||
|
|
||||
|
if (index + sizeof(T) > size) // TODO(deanm): integer overflow.
|
||||
|
return ThrowError("Index out of range."); |
||||
|
|
||||
|
void* ptr = args.This()->GetIndexedPropertiesExternalArrayData(); |
||||
|
setValue<T>(ptr, index, valueToCType<T>(args[1]), !little_endian); |
||||
|
return v8::Undefined(); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> getUint8(const v8::Arguments& args) { |
||||
|
return getGeneric<unsigned char>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> getInt8(const v8::Arguments& args) { |
||||
|
return getGeneric<char>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> getUint16(const v8::Arguments& args) { |
||||
|
return getGeneric<unsigned short>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> getInt16(const v8::Arguments& args) { |
||||
|
return getGeneric<short>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> getUint32(const v8::Arguments& args) { |
||||
|
return getGeneric<unsigned int>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> getInt32(const v8::Arguments& args) { |
||||
|
return getGeneric<int>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> getFloat32(const v8::Arguments& args) { |
||||
|
return getGeneric<float>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> getFloat64(const v8::Arguments& args) { |
||||
|
return getGeneric<double>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> setUint8(const v8::Arguments& args) { |
||||
|
return setGeneric<unsigned char>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> setInt8(const v8::Arguments& args) { |
||||
|
return setGeneric<char>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> setUint16(const v8::Arguments& args) { |
||||
|
return setGeneric<unsigned short>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> setInt16(const v8::Arguments& args) { |
||||
|
return setGeneric<short>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> setUint32(const v8::Arguments& args) { |
||||
|
return setGeneric<unsigned int>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> setInt32(const v8::Arguments& args) { |
||||
|
return setGeneric<int>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> setFloat32(const v8::Arguments& args) { |
||||
|
return setGeneric<float>(args); |
||||
|
} |
||||
|
|
||||
|
static v8::Handle<v8::Value> setFloat64(const v8::Arguments& args) { |
||||
|
return setGeneric<double>(args); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
} // namespace
|
||||
|
|
||||
|
namespace v8_typed_array { |
||||
|
|
||||
|
void AttachBindings(v8::Handle<v8::Object> obj) { |
||||
|
obj->Set(v8::String::New("ArrayBuffer"), |
||||
|
ArrayBuffer::GetTemplate()->GetFunction()); |
||||
|
obj->Set(v8::String::New("Int8Array"), |
||||
|
Int8Array::GetTemplate()->GetFunction()); |
||||
|
obj->Set(v8::String::New("Uint8Array"), |
||||
|
Uint8Array::GetTemplate()->GetFunction()); |
||||
|
obj->Set(v8::String::New("Int16Array"), |
||||
|
Int16Array::GetTemplate()->GetFunction()); |
||||
|
obj->Set(v8::String::New("Uint16Array"), |
||||
|
Uint16Array::GetTemplate()->GetFunction()); |
||||
|
obj->Set(v8::String::New("Int32Array"), |
||||
|
Int32Array::GetTemplate()->GetFunction()); |
||||
|
obj->Set(v8::String::New("Uint32Array"), |
||||
|
Uint32Array::GetTemplate()->GetFunction()); |
||||
|
obj->Set(v8::String::New("Float32Array"), |
||||
|
Float32Array::GetTemplate()->GetFunction()); |
||||
|
obj->Set(v8::String::New("DataView"), |
||||
|
DataView::GetTemplate()->GetFunction()); |
||||
|
} |
||||
|
|
||||
|
int SizeOfArrayElementForType(v8::ExternalArrayType type) { |
||||
|
switch (type) { |
||||
|
case v8::kExternalByteArray: |
||||
|
case v8::kExternalUnsignedByteArray: |
||||
|
return 1; |
||||
|
case v8::kExternalShortArray: |
||||
|
case v8::kExternalUnsignedShortArray: |
||||
|
return 2; |
||||
|
case v8::kExternalIntArray: |
||||
|
case v8::kExternalUnsignedIntArray: |
||||
|
case v8::kExternalFloatArray: |
||||
|
return 4; |
||||
|
default: |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} // namespace v8_typed_array
|
@ -0,0 +1,35 @@ |
|||||
|
// V8 Typed Array implementation.
|
||||
|
// (c) Dean McNamee <dean@gmail.com>, 2011.
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
// of this software and associated documentation files (the "Software"), to
|
||||
|
// deal in the Software without restriction, including without limitation the
|
||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
|
// furnished to do so, subject to the following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be included in
|
||||
|
// all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
|
// IN THE SOFTWARE.
|
||||
|
|
||||
|
#ifndef V8_TYPED_ARRAY_H_ |
||||
|
#define V8_TYPED_ARRAY_H_ |
||||
|
|
||||
|
#include <v8.h> |
||||
|
|
||||
|
namespace v8_typed_array { |
||||
|
|
||||
|
void AttachBindings(v8::Handle<v8::Object> obj); |
||||
|
|
||||
|
int SizeOfArrayElementForType(v8::ExternalArrayType type); |
||||
|
|
||||
|
} // namespace v8_typed_array
|
||||
|
|
||||
|
#endif // V8_TYPED_ARRAY_H_
|
Loading…
Reference in new issue