Commit 6ce13906 authored by danno@chromium.org's avatar danno@chromium.org

Add primitive WebGL ArrayBuffer() support to d8

R=jkummerow@chromium.org
BUG=
TEST=

Review URL: http://codereview.chromium.org/9114050

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10389 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 430c64d1
// Copyright 2011 the V8 project authors. All rights reserved. // Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
...@@ -281,62 +281,160 @@ Handle<Value> Shell::Load(const Arguments& args) { ...@@ -281,62 +281,160 @@ Handle<Value> Shell::Load(const Arguments& args) {
return Undefined(); return Undefined();
} }
static size_t convertToUint(Local<Value> value_in, TryCatch* try_catch) {
if (value_in->IsUint32()) {
return value_in->Uint32Value();
}
Local<Value> number = value_in->ToNumber();
if (try_catch->HasCaught()) return 0;
ASSERT(number->IsNumber());
Local<Int32> int32 = number->ToInt32();
if (try_catch->HasCaught() || int32.IsEmpty()) return 0;
int32_t raw_value = int32->Int32Value();
if (try_catch->HasCaught()) return 0;
if (raw_value < 0) {
ThrowException(String::New("Array length must not be negative."));
return 0;
}
static const int kMaxLength = 0x3fffffff;
#ifndef V8_SHARED
ASSERT(kMaxLength == i::ExternalArray::kMaxLength);
#endif // V8_SHARED
if (raw_value > static_cast<int32_t>(kMaxLength)) {
ThrowException(
String::New("Array length exceeds maximum length."));
}
return static_cast<size_t>(raw_value);
}
const char kArrayBufferReferencePropName[] = "_is_array_buffer_";
const char kArrayBufferMarkerPropName[] = "_array_buffer_ref_";
Handle<Value> Shell::CreateExternalArray(const Arguments& args, Handle<Value> Shell::CreateExternalArray(const Arguments& args,
ExternalArrayType type, ExternalArrayType type,
size_t element_size) { size_t element_size) {
TryCatch try_catch;
bool is_array_buffer_construct = element_size == 0;
if (is_array_buffer_construct) {
type = v8::kExternalByteArray;
element_size = 1;
}
ASSERT(element_size == 1 || element_size == 2 || element_size == 4 || ASSERT(element_size == 1 || element_size == 2 || element_size == 4 ||
element_size == 8); element_size == 8);
if (args.Length() != 1) { if (args.Length() == 0) {
return ThrowException(
String::New("Array constructor must have at least one "
"parameter."));
}
bool first_arg_is_array_buffer =
args[0]->IsObject() &&
args[0]->ToObject()->Get(
String::New(kArrayBufferMarkerPropName))->IsTrue();
// Currently, only the following constructors are supported:
// TypedArray(unsigned long length)
// TypedArray(ArrayBuffer buffer,
// optional unsigned long byteOffset,
// optional unsigned long length)
if (args.Length() > 3) {
return ThrowException( return ThrowException(
String::New("Array constructor needs one parameter.")); String::New("Array constructor from ArrayBuffer must "
"have 1-3 parameters."));
} }
static const int kMaxLength = 0x3fffffff;
#ifndef V8_SHARED Local<Value> length_value = (args.Length() < 3)
ASSERT(kMaxLength == i::ExternalArray::kMaxLength); ? (first_arg_is_array_buffer
#endif // V8_SHARED ? args[0]->ToObject()->Get(String::New("length"))
size_t length = 0; : args[0])
TryCatch try_catch; : args[2];
if (args[0]->IsUint32()) { size_t length = convertToUint(length_value, &try_catch);
length = args[0]->Uint32Value(); if (try_catch.HasCaught()) return try_catch.Exception();
} else {
Local<Number> number = args[0]->ToNumber(); void* data = NULL;
if (number.IsEmpty()) { size_t offset = 0;
ASSERT(try_catch.HasCaught());
return try_catch.Exception(); Handle<Object> array = Object::New();
if (first_arg_is_array_buffer) {
Handle<Object> derived_from = args[0]->ToObject();
data = derived_from->GetIndexedPropertiesExternalArrayData();
size_t array_buffer_length = convertToUint(
derived_from->Get(String::New("length")),
&try_catch);
if (try_catch.HasCaught()) return try_catch.Exception();
if (data == NULL && array_buffer_length != 0) {
return ThrowException(
String::New("ArrayBuffer doesn't have data"));
} }
ASSERT(number->IsNumber());
Local<Int32> int32 = number->ToInt32(); if (args.Length() > 1) {
if (int32.IsEmpty()) { offset = convertToUint(args[1], &try_catch);
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) return try_catch.Exception();
return try_catch.Exception();
// The given byteOffset must be a multiple of the element size of the
// specific type, otherwise an exception is raised.
if (offset % element_size != 0) {
return ThrowException(
String::New("offset must be multiple of element_size"));
} }
} }
int32_t raw_length = int32->Int32Value();
if (try_catch.HasCaught()) { if (offset > array_buffer_length) {
return try_catch.Exception(); return ThrowException(
String::New("byteOffset must be less than ArrayBuffer length."));
} }
if (raw_length < 0) {
return ThrowException(String::New("Array length must not be negative.")); if (args.Length() == 2) {
// If length is not explicitly specified, the length of the ArrayBuffer
// minus the byteOffset must be a multiple of the element size of the
// specific type, or an exception is raised.
length = array_buffer_length - offset;
} }
if (raw_length > static_cast<int32_t>(kMaxLength)) {
if (args.Length() != 3) {
if (length % element_size != 0) {
return ThrowException( return ThrowException(
String::New("Array length exceeds maximum length.")); String::New("ArrayBuffer length minus the byteOffset must be a "
"multiple of the element size"));
} }
length = static_cast<size_t>(raw_length); length /= element_size;
} }
if (length > static_cast<size_t>(kMaxLength)) {
return ThrowException(String::New("Array length exceeds maximum length.")); // If a given byteOffset and length references an area beyond the end of
// the ArrayBuffer an exception is raised.
if (offset + (length * element_size) > array_buffer_length) {
return ThrowException(
String::New("length references an area beyond the end of the "
"ArrayBuffer"));
} }
void* data = calloc(length, element_size);
if (data == NULL) { // Hold a reference to the ArrayBuffer so its buffer doesn't get collected.
return ThrowException(String::New("Memory allocation failed.")); array->Set(String::New(kArrayBufferReferencePropName), args[0], ReadOnly);
} }
Handle<Object> array = Object::New();
if (is_array_buffer_construct) {
array->Set(String::New(kArrayBufferMarkerPropName), True(), ReadOnly);
}
Persistent<Object> persistent_array = Persistent<Object>::New(array); Persistent<Object> persistent_array = Persistent<Object>::New(array);
persistent_array.MakeWeak(data, ExternalArrayWeakCallback); persistent_array.MakeWeak(data, ExternalArrayWeakCallback);
persistent_array.MarkIndependent(); persistent_array.MarkIndependent();
array->SetIndexedPropertiesToExternalArrayData(data, type, if (data == NULL && length != 0) {
data = calloc(length, element_size);
if (data == NULL) {
return ThrowException(String::New("Memory allocation failed."));
}
}
array->SetIndexedPropertiesToExternalArrayData(
reinterpret_cast<uint8_t*>(data) + offset, type,
static_cast<int>(length)); static_cast<int>(length));
array->Set(String::New("length"), array->Set(String::New("length"),
Int32::New(static_cast<int32_t>(length)), ReadOnly); Int32::New(static_cast<int32_t>(length)), ReadOnly);
...@@ -347,11 +445,22 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, ...@@ -347,11 +445,22 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args,
void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) { void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) {
HandleScope scope;
Handle<String> prop_name = String::New(kArrayBufferReferencePropName);
Handle<Object> converted_object = object->ToObject();
Local<Value> prop_value = converted_object->Get(prop_name);
if (data != NULL && !prop_value->IsObject()) {
free(data); free(data);
}
object.Dispose(); object.Dispose();
} }
Handle<Value> Shell::ArrayBuffer(const Arguments& args) {
return CreateExternalArray(args, v8::kExternalByteArray, 0);
}
Handle<Value> Shell::Int8Array(const Arguments& args) { Handle<Value> Shell::Int8Array(const Arguments& args) {
return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t)); return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t));
} }
...@@ -693,6 +802,8 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() { ...@@ -693,6 +802,8 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() {
FunctionTemplate::New(DisableProfiler)); FunctionTemplate::New(DisableProfiler));
// Bind the handlers for external arrays. // Bind the handlers for external arrays.
global_template->Set(String::New("ArrayBuffer"),
FunctionTemplate::New(ArrayBuffer));
global_template->Set(String::New("Int8Array"), global_template->Set(String::New("Int8Array"),
FunctionTemplate::New(Int8Array)); FunctionTemplate::New(Int8Array));
global_template->Set(String::New("Uint8Array"), global_template->Set(String::New("Uint8Array"),
......
// Copyright 2011 the V8 project authors. All rights reserved. // Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
...@@ -289,6 +289,7 @@ class Shell : public i::AllStatic { ...@@ -289,6 +289,7 @@ class Shell : public i::AllStatic {
static Handle<Value> Read(const Arguments& args); static Handle<Value> Read(const Arguments& args);
static Handle<Value> ReadLine(const Arguments& args); static Handle<Value> ReadLine(const Arguments& args);
static Handle<Value> Load(const Arguments& args); static Handle<Value> Load(const Arguments& args);
static Handle<Value> ArrayBuffer(const Arguments& args);
static Handle<Value> Int8Array(const Arguments& args); static Handle<Value> Int8Array(const Arguments& args);
static Handle<Value> Uint8Array(const Arguments& args); static Handle<Value> Uint8Array(const Arguments& args);
static Handle<Value> Int16Array(const Arguments& args); static Handle<Value> Int16Array(const Arguments& args);
......
...@@ -43,6 +43,56 @@ f(a); ...@@ -43,6 +43,56 @@ f(a);
assertEquals(0, a[0]); assertEquals(0, a[0]);
assertEquals(0, a[1]); assertEquals(0, a[1]);
// No-parameter constructor should fail right now.
function abfunc1() {
return new ArrayBuffer();
}
assertThrows(abfunc1);
// Test derivation from an ArrayBuffer
var ab = new ArrayBuffer(12);
var derived_uint8 = new Uint8Array(ab);
assertEquals(12, derived_uint8.length);
var derived_uint32 = new Uint32Array(ab);
assertEquals(3, derived_uint32.length);
var derived_uint32_2 = new Uint32Array(ab,4);
assertEquals(2, derived_uint32_2.length);
var derived_uint32_3 = new Uint32Array(ab,4,1);
assertEquals(1, derived_uint32_3.length);
// Resulting array length of zero should fail.
function abfunc2() {
new Uint32Array(ab,3);
}
assertThrows(abfunc2);
// If a given byteOffset and length references an area beyond the end of the
// ArrayBuffer an exception is raised.
function abfunc3() {
new Uint32Array(ab,4,3);
}
assertThrows(abfunc3);
function abfunc4() {
new Uint32Array(ab,16);
}
assertThrows(abfunc4);
// The given byteOffset must be a multiple of the element size of the specific
// type, otherwise an exception is raised.
function abfunc5() {
new Uint32Array(ab,5);
}
assertThrows(abfunc5);
// If length is not explicitly specified, the length of the ArrayBuffer minus
// the byteOffset must be a multiple of the element size of the specific type,
// or an exception is raised.
var ab2 = new ArrayBuffer(13);
function abfunc6() {
new Uint32Array(ab2,4);
}
assertThrows(abfunc6);
// Test the correct behavior of the |BYTES_PER_ELEMENT| property (which is // Test the correct behavior of the |BYTES_PER_ELEMENT| property (which is
// "constant", but not read-only). // "constant", but not read-only).
a = new Int32Array(2); a = new Int32Array(2);
......
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