Revision: 14770
Author:   [email protected]
Date:     Thu May 23 03:01:42 2013
Log:      Externalization API for ArrayBuffer

[email protected]

Review URL: https://codereview.chromium.org/15001041
http://code.google.com/p/v8/source/detail?r=14770

Modified:
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/bootstrapper.cc
 /branches/bleeding_edge/src/d8.cc
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/test/cctest/test-api.cc

=======================================
--- /branches/bleeding_edge/include/v8.h        Thu May 23 01:19:27 2013
+++ /branches/bleeding_edge/include/v8.h        Thu May 23 03:01:42 2013
@@ -2325,6 +2325,33 @@
   static void CheckCast(Value* obj);
 };

+/**
+ * The contents of an |ArrayBuffer|. Externalization of |ArrayBuffer|
+ * populates an instance of this class with a pointer to data and byte length.
+ *
+ * |ArrayBufferContents| is the owner of its data. When an instance of
+ * this class is destructed, the |Data| is freed.
+ *
+ * This API is experimental and may change significantly.
+ */
+class V8EXPORT ArrayBufferContents {
+ public:
+  ArrayBufferContents() : data_(NULL), byte_length_(0) {}
+  ~ArrayBufferContents();
+
+  void* Data() const { return data_; }
+  size_t ByteLength() const { return byte_length_; }
+
+ private:
+  void* data_;
+  size_t byte_length_;
+
+  friend class ArrayBuffer;
+};
+
+#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT
+#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2
+#endif

 /**
  * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5).
@@ -2336,27 +2363,40 @@
    * Data length in bytes.
    */
   size_t ByteLength() const;
-  /**
-   * Raw pointer to the array buffer data
-   */
-  void* Data() const;

   /**
    * Create a new ArrayBuffer. Allocate |byte_length| bytes.
    * Allocated memory will be owned by a created ArrayBuffer and
-   * will be deallocated when it is garbage-collected.
+   * will be deallocated when it is garbage-collected,
+   * unless the object is externalized.
    */
   static Local<ArrayBuffer> New(size_t byte_length);

   /**
    * Create a new ArrayBuffer over an existing memory block.
+   * The created array buffer is immediately in externalized state.
    * The memory block will not be reclaimed when a created ArrayBuffer
    * is garbage-collected.
    */
   static Local<ArrayBuffer> New(void* data, size_t byte_length);

+  /**
+   * Returns true if ArrayBuffer is extrenalized, that is, does not
+   * own its memory block.
+   */
+  bool IsExternal() const;
+
+  /**
+   * Pass the ownership of this ArrayBuffer's backing store to
+   * a given ArrayBufferContents.
+   */
+  void Externalize(ArrayBufferContents* contents);
+
   V8_INLINE(static ArrayBuffer* Cast(Value* obj));

+
+ static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
+
  private:
   ArrayBuffer();
   static void CheckCast(Value* obj);
=======================================
--- /branches/bleeding_edge/src/api.cc  Thu May 23 01:19:27 2013
+++ /branches/bleeding_edge/src/api.cc  Thu May 23 03:01:42 2013
@@ -6016,19 +6016,37 @@
 }


-size_t v8::ArrayBuffer::ByteLength() const {
-  i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
-  if (IsDeadCheck(isolate, "v8::ArrayBuffer::ByteLength()")) return 0;
+bool v8::ArrayBuffer::IsExternal() const {
+  return Utils::OpenHandle(this)->is_external();
+}
+
+v8::ArrayBufferContents::~ArrayBufferContents() {
+  free(data_);
+  data_ = NULL;
+  byte_length_ = 0;
+}
+
+
+void v8::ArrayBuffer::Externalize(ArrayBufferContents* contents) {
   i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
-  return static_cast<size_t>(obj->byte_length()->Number());
+  ApiCheck(!obj->is_external(),
+            "v8::ArrayBuffer::Externalize",
+            "ArrayBuffer already externalized");
+  obj->set_is_external(true);
+  size_t byte_length = static_cast<size_t>(obj->byte_length()->Number());
+  ApiCheck(contents->data_ == NULL,
+           "v8::ArrayBuffer::Externalize",
+           "Externalizing into non-empty ArrayBufferContents");
+  contents->data_ = obj->backing_store();
+  contents->byte_length_ = byte_length;
 }


-void* v8::ArrayBuffer::Data() const {
+size_t v8::ArrayBuffer::ByteLength() const {
   i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
-  if (IsDeadCheck(isolate, "v8::ArrayBuffer::Data()")) return 0;
+  if (IsDeadCheck(isolate, "v8::ArrayBuffer::ByteLength()")) return 0;
   i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
-  return obj->backing_store();
+  return static_cast<size_t>(obj->byte_length()->Number());
 }


@@ -6051,7 +6069,7 @@
   ENTER_V8(isolate);
   i::Handle<i::JSArrayBuffer> obj =
       isolate->factory()->NewJSArrayBuffer();
-  i::Runtime::SetupArrayBuffer(isolate, obj, data, byte_length);
+  i::Runtime::SetupArrayBuffer(isolate, obj, true, data, byte_length);
   return Utils::ToLocal(obj);
 }

=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Tue May 21 05:03:49 2013
+++ /branches/bleeding_edge/src/bootstrapper.cc Thu May 23 03:01:42 2013
@@ -1320,10 +1320,12 @@
   if (FLAG_harmony_array_buffer) {
     // -- A r r a y B u f f e r
     Handle<JSFunction> array_buffer_fun =
-        InstallFunction(global, "ArrayBuffer", JS_ARRAY_BUFFER_TYPE,
-                        JSArrayBuffer::kSize,
-                        isolate()->initial_object_prototype(),
-                        Builtins::kIllegal, true, true);
+        InstallFunction(
+            global, "ArrayBuffer", JS_ARRAY_BUFFER_TYPE,
+            JSArrayBuffer::kSize +
+              v8::ArrayBuffer::kInternalFieldCount * kPointerSize,
+            isolate()->initial_object_prototype(),
+            Builtins::kIllegal, true, true);
     native_context()->set_array_buffer_fun(*array_buffer_fun);
   }

=======================================
--- /branches/bleeding_edge/src/d8.cc   Thu May 16 04:55:50 2013
+++ /branches/bleeding_edge/src/d8.cc   Thu May 23 03:01:42 2013
@@ -1048,6 +1048,16 @@
   return chars;
 }

+static void ReadBufferWeakCallback(v8::Isolate* isolate,
+                                   Persistent<Value>* object,
+                                   uint8_t* data) {
+  size_t byte_length = ArrayBuffer::Cast(**object)->ByteLength();
+  isolate->AdjustAmountOfExternalAllocatedMemory(
+      -static_cast<intptr_t>(byte_length));
+
+  delete[] data;
+  object->Dispose(isolate);
+}

 Handle<Value> Shell::ReadBuffer(const Arguments& args) {
   ASSERT(sizeof(char) == sizeof(uint8_t));  // NOLINT
@@ -1057,14 +1067,19 @@
     return Throw("Error loading file");
   }

+  Isolate* isolate = args.GetIsolate();
   uint8_t* data = reinterpret_cast<uint8_t*>(
       ReadChars(args.GetIsolate(), *filename, &length));
   if (data == NULL) {
     return Throw("Error reading file");
   }
-  Handle<v8::ArrayBuffer> buffer = ArrayBuffer::New(length);
-  memcpy(buffer->Data(), data, length);
-  delete[] data;
+  Handle<v8::ArrayBuffer> buffer = ArrayBuffer::New(data, length);
+  v8::Persistent<v8::Value> weak_handle =
+      v8::Persistent<v8::Value>::New(isolate, buffer);
+  weak_handle.MakeWeak(isolate, data, ReadBufferWeakCallback);
+  weak_handle.MarkIndependent();
+  isolate->AdjustAmountOfExternalAllocatedMemory(length);
+
   return buffer;
 }

=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Thu May 23 00:05:58 2013
+++ /branches/bleeding_edge/src/objects-inl.h   Thu May 23 03:01:42 2013
@@ -5313,6 +5313,17 @@


 ACCESSORS(JSArrayBuffer, byte_length, Object, kByteLengthOffset)
+ACCESSORS_TO_SMI(JSArrayBuffer, flag, kFlagOffset)
+
+
+bool JSArrayBuffer::is_external() {
+  return BooleanBit::get(flag(), kIsExternalBit);
+}
+
+
+void JSArrayBuffer::set_is_external(bool value) {
+  set_flag(BooleanBit::set(flag(), kIsExternalBit, value));
+}


 ACCESSORS(JSTypedArray, buffer, Object, kBufferOffset)
@@ -5320,7 +5331,6 @@
 ACCESSORS(JSTypedArray, byte_length, Object, kByteLengthOffset)
 ACCESSORS(JSTypedArray, length, Object, kLengthOffset)

-
 ACCESSORS(JSRegExp, data, Object, kDataOffset)


=======================================
--- /branches/bleeding_edge/src/objects.h       Thu May 23 02:19:18 2013
+++ /branches/bleeding_edge/src/objects.h       Thu May 23 03:01:42 2013
@@ -8763,6 +8763,12 @@
   // [byte_length]: length in bytes
   DECL_ACCESSORS(byte_length, Object)

+  // [flags]
+  DECL_ACCESSORS(flag, Smi)
+
+  inline bool is_external();
+  inline void set_is_external(bool value);
+
   // Casting.
   static inline JSArrayBuffer* cast(Object* obj);

@@ -8772,9 +8778,13 @@

   static const int kBackingStoreOffset = JSObject::kHeaderSize;
   static const int kByteLengthOffset = kBackingStoreOffset + kPointerSize;
-  static const int kSize = kByteLengthOffset + kPointerSize;
+  static const int kFlagOffset = kByteLengthOffset + kPointerSize;
+  static const int kSize = kFlagOffset + kPointerSize;

  private:
+  // Bit position in a flag
+  static const int kIsExternalBit = 0;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBuffer);
 };

=======================================
--- /branches/bleeding_edge/src/runtime.cc      Thu May 23 00:05:58 2013
+++ /branches/bleeding_edge/src/runtime.cc      Thu May 23 03:01:42 2013
@@ -658,28 +658,37 @@
   Isolate* isolate = reinterpret_cast<Isolate*>(external_isolate);
   HandleScope scope(isolate);
   Handle<Object> internal_object = Utils::OpenHandle(**object);
+ Handle<JSArrayBuffer> array_buffer(JSArrayBuffer::cast(*internal_object));

-  size_t allocated_length = NumberToSize(
-      isolate, JSArrayBuffer::cast(*internal_object)->byte_length());
-  isolate->heap()->AdjustAmountOfExternalAllocatedMemory(
-      -static_cast<intptr_t>(allocated_length));
-  if (data != NULL)
+  if (!array_buffer->is_external()) {
+    size_t allocated_length = NumberToSize(
+        isolate, array_buffer->byte_length());
+    isolate->heap()->AdjustAmountOfExternalAllocatedMemory(
+        -static_cast<intptr_t>(allocated_length));
     free(data);
+  }
   object->Dispose(external_isolate);
 }


-bool Runtime::SetupArrayBuffer(Isolate* isolate,
+void Runtime::SetupArrayBuffer(Isolate* isolate,
                                Handle<JSArrayBuffer> array_buffer,
+                               bool is_external,
                                void* data,
                                size_t allocated_length) {
+  ASSERT(array_buffer->GetInternalFieldCount() ==
+      v8::ArrayBuffer::kInternalFieldCount);
+  for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
+    array_buffer->SetInternalField(i, Smi::FromInt(0));
+  }
   array_buffer->set_backing_store(data);
+  array_buffer->set_flag(Smi::FromInt(0));
+  array_buffer->set_is_external(is_external);

   Handle<Object> byte_length =
       isolate->factory()->NewNumberFromSize(allocated_length);
   CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
   array_buffer->set_byte_length(*byte_length);
-  return true;
 }


@@ -696,8 +705,7 @@
     data = NULL;
   }

-  if (!SetupArrayBuffer(isolate, array_buffer, data, allocated_length))
-    return false;
+  SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length);

   v8::Isolate* external_isolate = reinterpret_cast<v8::Isolate*>(isolate);
   v8::Persistent<v8::Value> weak_handle = v8::Persistent<v8::Value>::New(
=======================================
--- /branches/bleeding_edge/src/runtime.h       Thu May 23 00:05:58 2013
+++ /branches/bleeding_edge/src/runtime.h       Thu May 23 03:01:42 2013
@@ -754,8 +754,9 @@
       Handle<Object> object,
       Handle<Object> key);

-  static bool SetupArrayBuffer(Isolate* isolate,
+  static void SetupArrayBuffer(Isolate* isolate,
                                Handle<JSArrayBuffer> array_buffer,
+                               bool is_external,
                                void* data,
                                size_t allocated_length);

=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc     Tue May 21 23:35:38 2013
+++ /branches/bleeding_edge/test/cctest/test-api.cc     Thu May 23 03:01:42 2013
@@ -2493,7 +2493,7 @@
 }


-THREADED_TEST(ArrayBuffer) {
+THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
   i::FLAG_harmony_array_buffer = true;
   i::FLAG_harmony_typed_arrays = true;

@@ -2502,10 +2502,16 @@
   v8::HandleScope handle_scope(isolate);

   Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(1024);
+  CHECK_EQ(1024, ab->ByteLength());
+  CHECK(!ab->IsExternal());
   HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);

-  CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
-  uint8_t* data = static_cast<uint8_t*>(ab->Data());
+  v8::ArrayBufferContents ab_contents;
+  ab->Externalize(&ab_contents);
+  CHECK(ab->IsExternal());
+
+  CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
+  uint8_t* data = static_cast<uint8_t*>(ab_contents.Data());
   ASSERT(data != NULL);
   env->Global()->Set(v8_str("ab"), ab);

@@ -2523,27 +2529,73 @@
   data[1] = 0x11;
   result = CompileRun("u8[0] + u8[1]");
   CHECK_EQ(0xDD, result->Int32Value());
+}

-  result = CompileRun("var ab1 = new ArrayBuffer(2);"
-                      "var u8_a = new Uint8Array(ab1);"
-                      "u8_a[0] = 0xAA;"
-                      "u8_a[1] = 0xFF; u8_a.buffer");
+
+THREADED_TEST(ArrayBuffer_JSInternalToExternal) {
+  i::FLAG_harmony_array_buffer = true;
+  i::FLAG_harmony_typed_arrays = true;
+
+  LocalContext env;
+  v8::Isolate* isolate = env->GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+
+
+  v8::Handle<v8::Value> result =
+      CompileRun("var ab1 = new ArrayBuffer(2);"
+                 "var u8_a = new Uint8Array(ab1);"
+                 "u8_a[0] = 0xAA;"
+                 "u8_a[1] = 0xFF; u8_a.buffer");
   Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::Cast(*result);
   CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
-  uint8_t* ab1_data = static_cast<uint8_t*>(ab1->Data());
-  CHECK_EQ(0xAA, ab1_data[0]);
+  CHECK(!ab1->IsExternal());
+  v8::ArrayBufferContents ab1_contents;
+  ab1->Externalize(&ab1_contents);
+  CHECK(ab1->IsExternal());
+
+  result = CompileRun("ab1.byteLength");
+  CHECK_EQ(2, result->Int32Value());
+  result = CompileRun("u8_a[0]");
+  CHECK_EQ(0xAA, result->Int32Value());
+  result = CompileRun("u8_a[1]");
+  CHECK_EQ(0xFF, result->Int32Value());
+  result = CompileRun("var u8_b = new Uint8Array(ab1);"
+                      "u8_b[0] = 0xBB;"
+                      "u8_a[0]");
+  CHECK_EQ(0xBB, result->Int32Value());
+  result = CompileRun("u8_b[1]");
+  CHECK_EQ(0xFF, result->Int32Value());
+
+  CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength()));
+  uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data());
+  CHECK_EQ(0xBB, ab1_data[0]);
   CHECK_EQ(0xFF, ab1_data[1]);
   ab1_data[0] = 0xCC;
   ab1_data[1] = 0x11;
   result = CompileRun("u8_a[0] + u8_a[1]");
   CHECK_EQ(0xDD, result->Int32Value());
+}

-  uint8_t* my_data = new uint8_t[100];
-  memset(my_data, 0, 100);
-  Local<v8::ArrayBuffer> ab3 = v8::ArrayBuffer::New(my_data, 100);
+
+THREADED_TEST(ArrayBuffer_External) {
+  i::FLAG_harmony_array_buffer = true;
+  i::FLAG_harmony_typed_arrays = true;
+
+  LocalContext env;
+  v8::Isolate* isolate = env->GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+
+  i::ScopedVector<uint8_t> my_data(100);
+  memset(my_data.start(), 0, 100);
+  Local<v8::ArrayBuffer> ab3 = v8::ArrayBuffer::New(my_data.start(), 100);
   CHECK_EQ(100, static_cast<int>(ab3->ByteLength()));
-  CHECK_EQ(my_data, ab3->Data());
+  CHECK(ab3->IsExternal());
+
   env->Global()->Set(v8_str("ab3"), ab3);
+
+  v8::Handle<v8::Value> result = CompileRun("ab3.byteLength");
+  CHECK_EQ(100, result->Int32Value());
+
   result = CompileRun("var u8_b = new Uint8Array(ab3);"
                       "u8_b[0] = 0xBB;"
                       "u8_b[1] = 0xCC;"
@@ -2555,12 +2607,7 @@
   my_data[1] = 0x11;
   result = CompileRun("u8_b[0] + u8_b[1]");
   CHECK_EQ(0xDD, result->Int32Value());
-
-  delete[] my_data;
 }
-
-
-


 THREADED_TEST(HiddenProperties) {
@@ -15460,12 +15507,14 @@
   i::FLAG_harmony_array_buffer = true;
   i::FLAG_harmony_typed_arrays = true;

+  i::ScopedVector<ElementType> backing_store(kElementCount+2);
+
   LocalContext env;
   v8::Isolate* isolate = env->GetIsolate();
   v8::HandleScope handle_scope(isolate);

-  Local<v8::ArrayBuffer> ab =
-      v8::ArrayBuffer::New((kElementCount+2)*sizeof(ElementType));
+  Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
+      backing_store.start(), (kElementCount+2)*sizeof(ElementType));
   Local<TypedArray> ta =
       TypedArray::New(ab, 2*sizeof(ElementType), kElementCount);
   CHECK_EQ(kElementCount, static_cast<int>(ta->Length()));
@@ -15474,7 +15523,7 @@
            static_cast<int>(ta->ByteLength()));
   CHECK_EQ(ab, ta->Buffer());

-  ElementType* data = static_cast<ElementType*>(ab->Data()) + 2;
+  ElementType* data = backing_store.start() + 2;
   for (int i = 0; i < kElementCount; i++) {
     data[i] = static_cast<ElementType>(i);
   }

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to