Revision: 11698
Author:   [email protected]
Date:     Fri Jun  1 04:46:05 2012
Log:      Clean up d8 ArrayBuffer implementation and fix bug in readbuffer:

- Separate CreateExternalArrayBuffer function.
- Properly create buffers for arrays constructed with size argument only.
- Finalization of data array is tied to buffer object exclusively.
- Get rid of hidden buffer reference in array objects and size header in data.
- Use 'new' instead of 'malloc' in readbuffer.
- Test cases for additional array and buffer properties.

[email protected]
BUG=
TEST=

Review URL: https://chromiumcodereview.appspot.com/10459047
http://code.google.com/p/v8/source/detail?r=11698

Modified:
 /branches/bleeding_edge/src/d8.cc
 /branches/bleeding_edge/src/d8.h
 /branches/bleeding_edge/test/mjsunit/external-array.js

=======================================
--- /branches/bleeding_edge/src/d8.cc   Wed May 30 07:26:34 2012
+++ /branches/bleeding_edge/src/d8.cc   Fri Jun  1 04:46:05 2012
@@ -317,63 +317,82 @@


 const char kArrayBufferMarkerPropName[] = "d8::_is_array_buffer_";
-const char kArrayBufferReferencePropName[] = "d8::_array_buffer_ref_";
-
-static const int kExternalArrayAllocationHeaderSize = 2;
-
-Handle<Value> Shell::CreateExternalArray(const Arguments& args,
-                                         ExternalArrayType type,
-                                         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 ||
-         element_size == 8);
+
+
+Handle<Value> Shell::CreateExternalArrayBuffer(int32_t length) {
+  static const int32_t kMaxSize = 0x7fffffff;
+  // Make sure the total size fits into a (signed) int.
+  if (length < 0 || length > kMaxSize) {
+ return ThrowException(String::New("ArrayBuffer exceeds maximum size (2G)"));
+  }
+  uint8_t* data = new uint8_t[length];
+  if (data == NULL) {
+    return ThrowException(String::New("Memory allocation failed."));
+  }
+  memset(data, 0, length);
+
+  Handle<Object> buffer = Object::New();
+  buffer->SetHiddenValue(String::New(kArrayBufferMarkerPropName), True());
+  Persistent<Object> persistent_array = Persistent<Object>::New(buffer);
+  persistent_array.MakeWeak(data, ExternalArrayWeakCallback);
+  persistent_array.MarkIndependent();
+  V8::AdjustAmountOfExternalAllocatedMemory(length);
+
+  buffer->SetIndexedPropertiesToExternalArrayData(
+      data, v8::kExternalByteArray, length);
+  buffer->Set(String::New("byteLength"), Int32::New(length), ReadOnly);
+
+  return buffer;
+}
+
+
+Handle<Value> Shell::CreateExternalArrayBuffer(const Arguments& args) {
   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()->GetHiddenValue(
-          String::New(kArrayBufferMarkerPropName)).IsEmpty();
+        String::New("ArrayBuffer constructor must have one parameter."));
+  }
+  TryCatch try_catch;
+  int32_t length = convertToUint(args[0], &try_catch);
+  if (try_catch.HasCaught()) return try_catch.Exception();
+
+  return CreateExternalArrayBuffer(length);
+}
+
+
+Handle<Value> Shell::CreateExternalArray(const Arguments& args,
+                                         ExternalArrayType type,
+                                         int32_t element_size) {
+  TryCatch try_catch;
+  ASSERT(element_size == 1 || element_size == 2 ||
+         element_size == 4 || element_size == 8);
+
   // Currently, only the following constructors are supported:
-  //   ArrayBuffer(unsigned long length)
   //   TypedArray(unsigned long length)
   //   TypedArray(ArrayBuffer buffer,
   //              optional unsigned long byteOffset,
   //              optional unsigned long length)
-  size_t length;
-  size_t byteLength;
-  size_t byteOffset;
-  void* data = NULL;
-  Handle<Object> array = Object::New();
-  if (is_array_buffer_construct) {
-    byteLength = convertToUint(args[0], &try_catch);
-    if (try_catch.HasCaught()) return try_catch.Exception();
-    byteOffset = 0;
-    length = byteLength;
-
-    array->SetHiddenValue(String::New(kArrayBufferMarkerPropName), True());
-  } else if (first_arg_is_array_buffer) {
-    Handle<Object> buffer = args[0]->ToObject();
-    data = buffer->GetIndexedPropertiesExternalArrayData();
-    byteLength =
+  Handle<Object> buffer;
+  int32_t length;
+  int32_t byteLength;
+  int32_t byteOffset;
+  if (args.Length() == 0) {
+    return ThrowException(
+ String::New("Array constructor must have at least one parameter."));
+  }
+  if (args[0]->IsObject() &&
+     !args[0]->ToObject()->GetHiddenValue(
+         String::New(kArrayBufferMarkerPropName)).IsEmpty()) {
+    buffer = args[0]->ToObject();
+    int32_t bufferLength =
         convertToUint(buffer->Get(String::New("byteLength")), &try_catch);
     if (try_catch.HasCaught()) return try_catch.Exception();
-    if (data == NULL && byteLength != 0) {
-      return ThrowException(String::New("ArrayBuffer does not have data"));
-    }

     if (args.Length() < 2 || args[1]->IsUndefined()) {
       byteOffset = 0;
     } else {
       byteOffset = convertToUint(args[1], &try_catch);
       if (try_catch.HasCaught()) return try_catch.Exception();
-      if (byteOffset > byteLength) {
+      if (byteOffset > bufferLength) {
         return ThrowException(String::New("byteOffset out of bounds"));
       }
       if (byteOffset % element_size != 0) {
@@ -383,91 +402,56 @@
     }

     if (args.Length() < 3 || args[2]->IsUndefined()) {
+      byteLength = bufferLength - byteOffset;
+      length = byteLength / element_size;
       if (byteLength % element_size != 0) {
         return ThrowException(
             String::New("buffer size must be multiple of element_size"));
       }
-      length = (byteLength - byteOffset) / element_size;
     } else {
       length = convertToUint(args[2], &try_catch);
       if (try_catch.HasCaught()) return try_catch.Exception();
-    }
-
-    if (byteOffset + length * element_size > byteLength) {
-      return ThrowException(String::New("length out of bounds"));
-    }
-    byteLength = byteOffset + length * element_size;
-
- // Hold a reference to the ArrayBuffer so its buffer doesn't get collected.
-    array->SetHiddenValue(
-        String::New(kArrayBufferReferencePropName), args[0]);
+      byteLength = length * element_size;
+      if (byteOffset + byteLength > bufferLength) {
+        return ThrowException(String::New("length out of bounds"));
+      }
+    }
   } else {
     length = convertToUint(args[0], &try_catch);
     byteLength = length * element_size;
     byteOffset = 0;
+    Handle<Value> result = CreateExternalArrayBuffer(byteLength);
+    if (!result->IsObject()) return result;
+    buffer = result->ToObject();
   }

-  Persistent<Object> persistent_array = Persistent<Object>::New(array);
-  if (data == NULL && byteLength != 0) {
-    ASSERT(byteOffset == 0);
-    // Prepend the size of the allocated chunk to the data itself.
-    int total_size =
-        byteLength + kExternalArrayAllocationHeaderSize * sizeof(size_t);
-    static const int kMaxSize = 0x7fffffff;
-    // Make sure the total size fits into a (signed) int.
-    if (total_size > kMaxSize) {
- return ThrowException(String::New("Array exceeds maximum size (2G)"));
-    }
-    data = malloc(total_size);
-    if (data == NULL) {
-      return ThrowException(String::New("Memory allocation failed."));
-    }
-    *reinterpret_cast<size_t*>(data) = total_size;
- data = reinterpret_cast<size_t*>(data) + kExternalArrayAllocationHeaderSize;
-    memset(data, 0, byteLength);
-    V8::AdjustAmountOfExternalAllocatedMemory(total_size);
-  }
-  persistent_array.MakeWeak(data, ExternalArrayWeakCallback);
-  persistent_array.MarkIndependent();
-
+  void* data = buffer->GetIndexedPropertiesExternalArrayData();
+  ASSERT(data != NULL);
+
+  Handle<Object> array = Object::New();
   array->SetIndexedPropertiesToExternalArrayData(
-      reinterpret_cast<uint8_t*>(data) + byteOffset, type,
-      static_cast<int>(length));
-  array->Set(String::New("byteLength"),
-             Int32::New(static_cast<int32_t>(byteLength)), ReadOnly);
-  if (!is_array_buffer_construct) {
-    array->Set(String::New("byteOffset"),
-               Int32::New(static_cast<int32_t>(byteOffset)), ReadOnly);
-    array->Set(String::New("length"),
-               Int32::New(static_cast<int32_t>(length)), ReadOnly);
-    array->Set(String::New("BYTES_PER_ELEMENT"),
-               Int32::New(static_cast<int32_t>(element_size)));
- // We currently support 'buffer' property only if constructed from a buffer.
-    if (first_arg_is_array_buffer) {
-      array->Set(String::New("buffer"), args[0], ReadOnly);
-    }
-  }
+      static_cast<uint8_t*>(data) + byteOffset, type, length);
+  array->Set(String::New("byteLength"), Int32::New(byteLength), ReadOnly);
+  array->Set(String::New("byteOffset"), Int32::New(byteOffset), ReadOnly);
+  array->Set(String::New("length"), Int32::New(length), ReadOnly);
+  array->Set(String::New("BYTES_PER_ELEMENT"), Int32::New(element_size));
+  array->Set(String::New("buffer"), buffer, ReadOnly);
+
   return array;
 }


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->GetHiddenValue(prop_name);
-  if (data != NULL && prop_value.IsEmpty()) {
- data = reinterpret_cast<size_t*>(data) - kExternalArrayAllocationHeaderSize;
-    V8::AdjustAmountOfExternalAllocatedMemory(
-        -static_cast<int>(*reinterpret_cast<size_t*>(data)));
-    free(data);
-  }
+  Local<Value> length = object->ToObject()->Get(String::New("byteLength"));
+  V8::AdjustAmountOfExternalAllocatedMemory(-length->Uint32Value());
+  delete[] static_cast<uint8_t*>(data);
   object.Dispose();
 }


 Handle<Value> Shell::ArrayBuffer(const Arguments& args) {
-  return CreateExternalArray(args, v8::kExternalByteArray, 0);
+  return CreateExternalArrayBuffer(args);
 }


@@ -1035,27 +1019,28 @@


 Handle<Value> Shell::ReadBuffer(const Arguments& args) {
+  STATIC_ASSERT(sizeof(char) == sizeof(uint8_t));  // NOLINT
   String::Utf8Value filename(args[0]);
   int length;
   if (*filename == NULL) {
     return ThrowException(String::New("Error loading file"));
   }
-  char* data = ReadChars(*filename, &length);
+
+ uint8_t* data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
   if (data == NULL) {
     return ThrowException(String::New("Error reading file"));
   }
-
   Handle<Object> buffer = Object::New();
   buffer->SetHiddenValue(String::New(kArrayBufferMarkerPropName), True());
-
   Persistent<Object> persistent_buffer = Persistent<Object>::New(buffer);
   persistent_buffer.MakeWeak(data, ExternalArrayWeakCallback);
   persistent_buffer.MarkIndependent();
+  V8::AdjustAmountOfExternalAllocatedMemory(length);

   buffer->SetIndexedPropertiesToExternalArrayData(
- reinterpret_cast<uint8_t*>(data), kExternalUnsignedByteArray, length);
+      data, kExternalUnsignedByteArray, length);
   buffer->Set(String::New("byteLength"),
-             Int32::New(static_cast<int32_t>(length)), ReadOnly);
+      Int32::New(static_cast<int32_t>(length)), ReadOnly);
   return buffer;
 }

@@ -1220,7 +1205,7 @@

 Handle<String> SourceGroup::ReadFile(const char* name) {
   int size;
-  const char* chars = ReadChars(name, &size);
+  char* chars = ReadChars(name, &size);
   if (chars == NULL) return Handle<String>();
   Handle<String> result = String::New(chars, size);
   delete[] chars;
=======================================
--- /branches/bleeding_edge/src/d8.h    Tue May 22 05:49:20 2012
+++ /branches/bleeding_edge/src/d8.h    Fri Jun  1 04:46:05 2012
@@ -383,9 +383,11 @@
   static void RunShell();
   static bool SetOptions(int argc, char* argv[]);
   static Handle<ObjectTemplate> CreateGlobalTemplate();
+  static Handle<Value> CreateExternalArrayBuffer(int32_t size);
+  static Handle<Value> CreateExternalArrayBuffer(const Arguments& args);
   static Handle<Value> CreateExternalArray(const Arguments& args,
                                            ExternalArrayType type,
-                                           size_t element_size);
+                                           int32_t element_size);
static void ExternalArrayWeakCallback(Persistent<Value> object, void* data);
 };

=======================================
--- /branches/bleeding_edge/test/mjsunit/external-array.js Wed Feb 15 23:58:07 2012 +++ /branches/bleeding_edge/test/mjsunit/external-array.js Fri Jun 1 04:46:05 2012
@@ -52,13 +52,53 @@
 // Test derivation from an ArrayBuffer
 var ab = new ArrayBuffer(12);
 var derived_uint8 = new Uint8Array(ab);
+assertSame(ab, derived_uint8.buffer);
 assertEquals(12, derived_uint8.length);
+assertEquals(12, derived_uint8.byteLength);
+assertEquals(0, derived_uint8.byteOffset);
+assertEquals(1, derived_uint8.BYTES_PER_ELEMENT);
+var derived_uint8_2 = new Uint8Array(ab,7);
+assertSame(ab, derived_uint8_2.buffer);
+assertEquals(5, derived_uint8_2.length);
+assertEquals(5, derived_uint8_2.byteLength);
+assertEquals(7, derived_uint8_2.byteOffset);
+assertEquals(1, derived_uint8_2.BYTES_PER_ELEMENT);
+var derived_int16 = new Int16Array(ab);
+assertSame(ab, derived_int16.buffer);
+assertEquals(6, derived_int16.length);
+assertEquals(12, derived_int16.byteLength);
+assertEquals(0, derived_int16.byteOffset);
+assertEquals(2, derived_int16.BYTES_PER_ELEMENT);
+var derived_int16_2 = new Int16Array(ab,6);
+assertSame(ab, derived_int16_2.buffer);
+assertEquals(3, derived_int16_2.length);
+assertEquals(6, derived_int16_2.byteLength);
+assertEquals(6, derived_int16_2.byteOffset);
+assertEquals(2, derived_int16_2.BYTES_PER_ELEMENT);
 var derived_uint32 = new Uint32Array(ab);
+assertSame(ab, derived_uint32.buffer);
 assertEquals(3, derived_uint32.length);
+assertEquals(12, derived_uint32.byteLength);
+assertEquals(0, derived_uint32.byteOffset);
+assertEquals(4, derived_uint32.BYTES_PER_ELEMENT);
 var derived_uint32_2 = new Uint32Array(ab,4);
+assertSame(ab, derived_uint32_2.buffer);
 assertEquals(2, derived_uint32_2.length);
+assertEquals(8, derived_uint32_2.byteLength);
+assertEquals(4, derived_uint32_2.byteOffset);
+assertEquals(4, derived_uint32_2.BYTES_PER_ELEMENT);
 var derived_uint32_3 = new Uint32Array(ab,4,1);
+assertSame(ab, derived_uint32_3.buffer);
 assertEquals(1, derived_uint32_3.length);
+assertEquals(4, derived_uint32_3.byteLength);
+assertEquals(4, derived_uint32_3.byteOffset);
+assertEquals(4, derived_uint32_3.BYTES_PER_ELEMENT);
+var derived_float64 = new Float64Array(ab,0,1);
+assertSame(ab, derived_float64.buffer);
+assertEquals(1, derived_float64.length);
+assertEquals(8, derived_float64.byteLength);
+assertEquals(0, derived_float64.byteOffset);
+assertEquals(8, derived_float64.BYTES_PER_ELEMENT);

// If a given byteOffset and length references an area beyond the end of the
 // ArrayBuffer an exception is raised.
@@ -87,6 +127,24 @@
 }
 assertThrows(abfunc6);

+// Test that an array constructed without an array buffer creates one properly.
+a = new Uint8Array(31);
+assertEquals(a.byteLength, a.buffer.byteLength);
+assertEquals(a.length, a.buffer.byteLength);
+assertEquals(a.length * a.BYTES_PER_ELEMENT, a.buffer.byteLength);
+a = new Int16Array(5);
+assertEquals(a.byteLength, a.buffer.byteLength);
+assertEquals(a.length * a.BYTES_PER_ELEMENT, a.buffer.byteLength);
+a = new Float64Array(7);
+assertEquals(a.byteLength, a.buffer.byteLength);
+assertEquals(a.length * a.BYTES_PER_ELEMENT, a.buffer.byteLength);
+
+// Test that an implicitly created buffer is a valid buffer.
+a = new Float64Array(7);
+assertSame(a.buffer, (new Uint16Array(a.buffer)).buffer);
+assertSame(a.buffer, (new Float32Array(a.buffer,4)).buffer);
+assertSame(a.buffer, (new Int8Array(a.buffer,3,51)).buffer);
+
 // Test the correct behavior of the |BYTES_PER_ELEMENT| property (which is
 // "constant", but not read-only).
 a = new Int32Array(2);
@@ -351,3 +409,25 @@
 %OptimizeFunctionOnNextCall(store_float64_undefined);
 store_float64_undefined(float64_array);
 assertTrue(isNaN(float64_array[0]));
+
+
+// Check handling of 0-sized buffers and arrays.
+
+ab = new ArrayBuffer(0);
+assertEquals(0, ab.byteLength);
+a = new Int8Array(ab);
+assertEquals(0, a.byteLength);
+assertEquals(0, a.length);
+a[0] = 1;
+assertEquals(undefined, a[0])
+ab = new ArrayBuffer(16);
+a = new Float32Array(ab,4,0);
+assertEquals(0, a.byteLength);
+assertEquals(0, a.length);
+a[0] = 1;
+assertEquals(undefined, a[0])
+a = new Uint16Array(0);
+assertEquals(0, a.byteLength);
+assertEquals(0, a.length);
+a[0] = 1;
+assertEquals(undefined, a[0])

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to