Revision: 14438
Author:   [email protected]
Date:     Thu Apr 25 05:02:23 2013
Log:      First cut at API for ES6 ArrayBuffers

[email protected]
BUG=

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

Modified:
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/api.h
 /branches/bleeding_edge/src/bootstrapper.cc
 /branches/bleeding_edge/src/contexts.h
 /branches/bleeding_edge/src/factory.cc
 /branches/bleeding_edge/src/factory.h
 /branches/bleeding_edge/src/heap.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        Wed Apr 24 08:59:23 2013
+++ /branches/bleeding_edge/include/v8.h        Thu Apr 25 05:02:23 2013
@@ -2003,6 +2003,43 @@
 };


+/**
+ * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5).
+ * This API is experimental and may change significantly.
+ */
+class V8EXPORT ArrayBuffer : public Object {
+ public:
+  /**
+   * 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.
+   */
+  static Local<ArrayBuffer> New(size_t byte_length);
+
+  /**
+   * Create a new ArrayBuffer over an existing memory block.
+   * The memory block will not be reclaimed when a created ArrayBuffer
+   * is garbage-collected.
+   */
+  static Local<ArrayBuffer> New(void* data, size_t byte_length);
+
+  V8_INLINE(static ArrayBuffer* Cast(Value* obj));
+
+ private:
+  ArrayBuffer();
+  static void CheckCast(Value* obj);
+};
+
+
 /**
  * An instance of the built-in Date constructor (ECMA-262, 15.9).
  */
@@ -4499,7 +4536,7 @@
   static const int kJSObjectHeaderSize = 3 * kApiPointerSize;
   static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
   static const int kContextHeaderSize = 2 * kApiPointerSize;
-  static const int kContextEmbedderDataIndex = 55;
+  static const int kContextEmbedderDataIndex = 56;
   static const int kFullStringRepresentationMask = 0x07;
   static const int kStringEncodingMask = 0x4;
   static const int kExternalTwoByteRepresentationTag = 0x02;
@@ -5170,6 +5207,14 @@
 #endif
   return static_cast<Array*>(value);
 }
+
+
+ArrayBuffer* ArrayBuffer::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<ArrayBuffer*>(value);
+}


 Function* Function::Cast(v8::Value* value) {
=======================================
--- /branches/bleeding_edge/src/api.cc  Wed Apr 24 08:59:23 2013
+++ /branches/bleeding_edge/src/api.cc  Thu Apr 25 05:02:23 2013
@@ -52,6 +52,7 @@
 #include "profile-generator-inl.h"
 #include "property-details.h"
 #include "property.h"
+#include "runtime.h"
 #include "runtime-profiler.h"
 #include "scanner-character-streams.h"
 #include "snapshot.h"
@@ -2743,6 +2744,15 @@
            "v8::Array::Cast()",
            "Could not convert to array");
 }
+
+
+void v8::ArrayBuffer::CheckCast(Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::ArrayBuffer::Cast()")) return;
+  i::Handle<i::Object> obj = Utils::OpenHandle(that);
+  ApiCheck(obj->IsJSArrayBuffer(),
+           "v8::ArrayBuffer::Cast()",
+           "Could not convert to ArrayBuffer");
+}


 void v8::Date::CheckCast(v8::Value* that) {
@@ -5781,6 +5791,46 @@
   EXCEPTION_BAILOUT_CHECK(isolate, Local<Object>());
   return Utils::ToLocal(result);
 }
+
+
+size_t v8::ArrayBuffer::ByteLength() const {
+  i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+  if (IsDeadCheck(isolate, "v8::ArrayBuffer::ByteLength()")) return 0;
+  i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
+  return static_cast<size_t>(obj->byte_length()->Number());
+}
+
+
+void* v8::ArrayBuffer::Data() const {
+  i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+  if (IsDeadCheck(isolate, "v8::ArrayBuffer::Data()")) return 0;
+  i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
+  return obj->backing_store();
+}
+
+
+Local<ArrayBuffer> v8::ArrayBuffer::New(size_t byte_length) {
+  i::Isolate* isolate = i::Isolate::Current();
+  EnsureInitializedForIsolate(isolate, "v8::ArrayBuffer::New(size_t)");
+  LOG_API(isolate, "v8::ArrayBuffer::New(size_t)");
+  ENTER_V8(isolate);
+  i::Handle<i::JSArrayBuffer> obj =
+      isolate->factory()->NewJSArrayBuffer();
+  i::Runtime::SetupArrayBufferAllocatingData(isolate, obj, byte_length);
+  return Utils::ToLocal(obj);
+}
+
+
+Local<ArrayBuffer> v8::ArrayBuffer::New(void* data, size_t byte_length) {
+  i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::ArrayBuffer::New(void*, size_t)");
+  LOG_API(isolate, "v8::ArrayBuffer::New(void*, size_t)");
+  ENTER_V8(isolate);
+  i::Handle<i::JSArrayBuffer> obj =
+      isolate->factory()->NewJSArrayBuffer();
+  i::Runtime::SetupArrayBuffer(isolate, obj, data, byte_length);
+  return Utils::ToLocal(obj);
+}


 Local<Symbol> v8::Symbol::New(Isolate* isolate) {
=======================================
--- /branches/bleeding_edge/src/api.h   Wed Apr 10 05:16:29 2013
+++ /branches/bleeding_edge/src/api.h   Thu Apr 25 05:02:23 2013
@@ -170,6 +170,7 @@
   V(RegExp, JSRegExp)                          \
   V(Object, JSObject)                          \
   V(Array, JSArray)                            \
+  V(ArrayBuffer, JSArrayBuffer)                \
   V(String, String)                            \
   V(Symbol, Symbol)                            \
   V(Script, Object)                            \
@@ -205,6 +206,8 @@
       v8::internal::Handle<v8::internal::JSObject> obj);
   static inline Local<Array> ToLocal(
       v8::internal::Handle<v8::internal::JSArray> obj);
+  static inline Local<ArrayBuffer> ToLocal(
+      v8::internal::Handle<v8::internal::JSArrayBuffer> obj);
   static inline Local<Message> MessageToLocal(
       v8::internal::Handle<v8::internal::Object> obj);
   static inline Local<StackTrace> StackTraceToLocal(
@@ -275,6 +278,7 @@
 MAKE_TO_LOCAL(ToLocal, JSRegExp, RegExp)
 MAKE_TO_LOCAL(ToLocal, JSObject, Object)
 MAKE_TO_LOCAL(ToLocal, JSArray, Array)
+MAKE_TO_LOCAL(ToLocal, JSArrayBuffer, ArrayBuffer)
 MAKE_TO_LOCAL(ToLocal, FunctionTemplateInfo, FunctionTemplate)
 MAKE_TO_LOCAL(ToLocal, ObjectTemplateInfo, ObjectTemplate)
 MAKE_TO_LOCAL(ToLocal, SignatureInfo, Signature)
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Tue Apr 16 07:16:30 2013
+++ /branches/bleeding_edge/src/bootstrapper.cc Thu Apr 25 05:02:23 2013
@@ -1303,10 +1303,12 @@

   if (FLAG_harmony_typed_arrays) {
     {  // -- A r r a y B u f f e r
-      InstallFunction(global, "__ArrayBuffer", JS_ARRAY_BUFFER_TYPE,
-                      JSArrayBuffer::kSize,
-                      isolate()->initial_object_prototype(),
-                      Builtins::kIllegal, true);
+      Handle<JSFunction> array_buffer_fun =
+          InstallFunction(global, "__ArrayBuffer", JS_ARRAY_BUFFER_TYPE,
+                          JSArrayBuffer::kSize,
+                          isolate()->initial_object_prototype(),
+                          Builtins::kIllegal, true);
+      native_context()->set_array_buffer_fun(*array_buffer_fun);
     }
     {
       // -- T y p e d A r r a y s
=======================================
--- /branches/bleeding_edge/src/contexts.h      Thu Apr 11 09:28:19 2013
+++ /branches/bleeding_edge/src/contexts.h      Thu Apr 25 05:02:23 2013
@@ -123,6 +123,7 @@
   V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
   V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \
   V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \
+  V(ARRAY_BUFFER_FUN_INDEX, JSFunction, array_buffer_fun) \
   V(FUNCTION_MAP_INDEX, Map, function_map) \
   V(STRICT_MODE_FUNCTION_MAP_INDEX, Map, strict_mode_function_map) \
V(FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, Map, function_without_prototype_map) \
@@ -276,6 +277,7 @@
     GLOBAL_EVAL_FUN_INDEX,
     INSTANTIATE_FUN_INDEX,
     CONFIGURE_INSTANCE_FUN_INDEX,
+    ARRAY_BUFFER_FUN_INDEX,
     MESSAGE_LISTENERS_INDEX,
     MAKE_MESSAGE_FUN_INDEX,
     GET_STACK_TRACE_LINE_INDEX,
=======================================
--- /branches/bleeding_edge/src/factory.cc      Tue Apr 23 02:23:07 2013
+++ /branches/bleeding_edge/src/factory.cc      Thu Apr 25 05:02:23 2013
@@ -1044,6 +1044,16 @@
       isolate(),
       array->EnsureCanContainElements(*elements, length, mode));
 }
+
+
+Handle<JSArrayBuffer> Factory::NewJSArrayBuffer() {
+  JSFunction* array_buffer_fun =
+      isolate()->context()->native_context()->array_buffer_fun();
+  CALL_HEAP_FUNCTION(
+      isolate(),
+      isolate()->heap()->AllocateJSObject(array_buffer_fun),
+      JSArrayBuffer);
+}


 Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
=======================================
--- /branches/bleeding_edge/src/factory.h       Thu Apr 18 02:50:46 2013
+++ /branches/bleeding_edge/src/factory.h       Thu Apr 25 05:02:23 2013
@@ -313,6 +313,8 @@
                                 uint32_t length,
                                 EnsureElementsMode mode);

+  Handle<JSArrayBuffer> NewJSArrayBuffer();
+
Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);

// Change the type of the argument into a JS object/function and reinitialize.
=======================================
--- /branches/bleeding_edge/src/heap.h  Thu Apr 25 02:17:07 2013
+++ /branches/bleeding_edge/src/heap.h  Thu Apr 25 05:02:23 2013
@@ -693,6 +693,12 @@
   // Please note this does not perform a garbage collection.
MUST_USE_RESULT MaybeObject* AllocateFunctionPrototype(JSFunction* function);

+  // Allocates a JS ArrayBuffer object.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+  // failed.
+  // Please note this does not perform a garbage collection.
+  MUST_USE_RESULT MaybeObject* AllocateJSArrayBuffer();
+
   // Allocates a Harmony proxy or function proxy.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
   // failed.
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Thu Apr 25 03:59:09 2013
+++ /branches/bleeding_edge/src/runtime.cc      Thu Apr 25 05:02:23 2013
@@ -660,6 +660,47 @@
     free(data);
   object.Dispose(external_isolate);
 }
+
+
+bool Runtime::SetupArrayBuffer(Isolate* isolate,
+                               Handle<JSArrayBuffer> array_buffer,
+                               void* data,
+                               size_t allocated_length) {
+  array_buffer->set_backing_store(data);
+
+  Handle<Object> byte_length =
+      isolate->factory()->NewNumber(static_cast<double>(allocated_length));
+  CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
+  array_buffer->set_byte_length(*byte_length);
+  return true;
+}
+
+
+bool Runtime::SetupArrayBufferAllocatingData(
+    Isolate* isolate,
+    Handle<JSArrayBuffer> array_buffer,
+    size_t allocated_length) {
+  void* data;
+  if (allocated_length != 0) {
+    data = malloc(allocated_length);
+    if (data == NULL) return false;
+    memset(data, 0, allocated_length);
+  } else {
+    data = NULL;
+  }
+
+  if (!SetupArrayBuffer(isolate, array_buffer, data, allocated_length))
+    return false;
+
+  v8::Isolate* external_isolate = reinterpret_cast<v8::Isolate*>(isolate);
+  v8::Persistent<v8::Value> weak_handle = v8::Persistent<v8::Value>::New(
+ external_isolate, v8::Utils::ToLocal(Handle<Object>::cast(array_buffer)));
+  weak_handle.MakeWeak(external_isolate, data, ArrayBufferWeakCallback);
+  weak_handle.MarkIndependent(external_isolate);
+  isolate->heap()->AdjustAmountOfExternalAllocatedMemory(allocated_length);
+
+  return true;
+}


 RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferInitialize) {
@@ -685,38 +726,12 @@
     allocated_length = static_cast<size_t>(value);
   }

-  void* data;
-  if (allocated_length != 0) {
-    data = malloc(allocated_length);
-
-    if (data == NULL) {
+  if (!Runtime::SetupArrayBufferAllocatingData(isolate,
+                                               holder, allocated_length)) {
       return isolate->Throw(*isolate->factory()->
           NewRangeError("invalid_array_buffer_length",
             HandleVector<Object>(NULL, 0)));
-    }
-
-    memset(data, 0, allocated_length);
-  } else {
-    data = NULL;
-  }
-  holder->set_backing_store(data);
-
-  Object* byte_length;
-  {
-    MaybeObject* maybe_byte_length =
-        isolate->heap()->NumberFromDouble(
-            static_cast<double>(allocated_length));
- if (!maybe_byte_length->ToObject(&byte_length)) return maybe_byte_length;
   }
-  CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
-  holder->set_byte_length(byte_length);
-
-  v8::Isolate* external_isolate = reinterpret_cast<v8::Isolate*>(isolate);
-  v8::Persistent<v8::Value> weak_handle = v8::Persistent<v8::Value>::New(
-      external_isolate, v8::Utils::ToLocal(Handle<Object>::cast(holder)));
-  weak_handle.MakeWeak(external_isolate, data, ArrayBufferWeakCallback);
-  weak_handle.MarkIndependent(external_isolate);
-  isolate->heap()->AdjustAmountOfExternalAllocatedMemory(allocated_length);

   return *holder;
 }
=======================================
--- /branches/bleeding_edge/src/runtime.h       Wed Apr 24 06:00:16 2013
+++ /branches/bleeding_edge/src/runtime.h       Thu Apr 25 05:02:23 2013
@@ -748,6 +748,16 @@
       Handle<Object> object,
       Handle<Object> key);

+  static bool SetupArrayBuffer(Isolate* isolate,
+                               Handle<JSArrayBuffer> array_buffer,
+                               void* data,
+                               size_t allocated_length);
+
+  static bool SetupArrayBufferAllocatingData(
+      Isolate* isolate,
+      Handle<JSArrayBuffer> array_buffer,
+      size_t allocated_length);
+
   // Helper functions used stubs.
   static void PerformGC(Object* result);

=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc     Wed Apr 24 08:59:23 2013
+++ /branches/bleeding_edge/test/cctest/test-api.cc     Thu Apr 25 05:02:23 2013
@@ -2251,6 +2251,73 @@
   CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
   CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
 }
+
+
+THREADED_TEST(ArrayBuffer) {
+  i::FLAG_harmony_typed_arrays = true;
+
+  LocalContext env;
+  v8::Isolate* isolate = env->GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+
+  Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(1024);
+  HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+  CHECK_EQ(1024, ab->ByteLength());
+  uint8_t* data = static_cast<uint8_t*>(ab->Data());
+  ASSERT(data != NULL);
+  env->Global()->Set(v8_str("ab"), ab);
+
+  v8::Handle<v8::Value> result = CompileRun("ab.byteLength");
+  CHECK_EQ(1024, result->Int32Value());
+
+  result = CompileRun("var u8 = new __Uint8Array(ab);"
+                      "u8[0] = 0xFF;"
+                      "u8[1] = 0xAA;"
+                      "u8.length");
+  CHECK_EQ(1024, result->Int32Value());
+  CHECK_EQ(0xFF, data[0]);
+  CHECK_EQ(0xAA, data[1]);
+  data[0] = 0xCC;
+  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");
+  Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::Cast(*result);
+  CHECK_EQ(2, ab1->ByteLength());
+  uint8_t* ab1_data = static_cast<uint8_t*>(ab1->Data());
+  CHECK_EQ(0xAA, 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);
+  CHECK_EQ(100, ab3->ByteLength());
+  CHECK_EQ(my_data, ab3->Data());
+  env->Global()->Set(v8_str("ab3"), ab3);
+  result = CompileRun("var u8_b = new __Uint8Array(ab3);"
+                      "u8_b[0] = 0xBB;"
+                      "u8_b[1] = 0xCC;"
+                      "u8_b.length");
+  CHECK_EQ(100, result->Int32Value());
+  CHECK_EQ(0xBB, my_data[0]);
+  CHECK_EQ(0xCC, my_data[1]);
+  my_data[0] = 0xCC;
+  my_data[1] = 0x11;
+  result = CompileRun("u8_b[0] + u8_b[1]");
+  CHECK_EQ(0xDD, result->Int32Value());
+
+
+  delete[] my_data;
+}


 THREADED_TEST(HiddenProperties) {

--
--
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