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.