Externalization API for ArrayBuffer

R=svenpanne@chromium.org

Review URL: https://codereview.chromium.org/15001041

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14770 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dslomov@chromium.org 2013-05-23 10:01:42 +00:00
parent a1e18bdf3c
commit fc73052dc2
9 changed files with 207 additions and 54 deletions

View File

@ -2325,6 +2325,33 @@ class V8EXPORT Function : public Object {
static void CheckCast(Value* obj); 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). * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5).
@ -2336,27 +2363,40 @@ class V8EXPORT ArrayBuffer : public Object {
* Data length in bytes. * Data length in bytes.
*/ */
size_t ByteLength() const; size_t ByteLength() const;
/**
* Raw pointer to the array buffer data
*/
void* Data() const;
/** /**
* Create a new ArrayBuffer. Allocate |byte_length| bytes. * Create a new ArrayBuffer. Allocate |byte_length| bytes.
* Allocated memory will be owned by a created ArrayBuffer and * 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); static Local<ArrayBuffer> New(size_t byte_length);
/** /**
* Create a new ArrayBuffer over an existing memory block. * 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 * The memory block will not be reclaimed when a created ArrayBuffer
* is garbage-collected. * is garbage-collected.
*/ */
static Local<ArrayBuffer> New(void* data, size_t byte_length); 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)); V8_INLINE(static ArrayBuffer* Cast(Value* obj));
static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
private: private:
ArrayBuffer(); ArrayBuffer();
static void CheckCast(Value* obj); static void CheckCast(Value* obj);

View File

@ -6016,6 +6016,32 @@ Local<Object> Array::CloneElementAt(uint32_t index) {
} }
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);
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;
}
size_t v8::ArrayBuffer::ByteLength() const { size_t v8::ArrayBuffer::ByteLength() const {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
if (IsDeadCheck(isolate, "v8::ArrayBuffer::ByteLength()")) return 0; if (IsDeadCheck(isolate, "v8::ArrayBuffer::ByteLength()")) return 0;
@ -6024,14 +6050,6 @@ size_t v8::ArrayBuffer::ByteLength() const {
} }
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) { Local<ArrayBuffer> v8::ArrayBuffer::New(size_t byte_length) {
i::Isolate* isolate = i::Isolate::Current(); i::Isolate* isolate = i::Isolate::Current();
EnsureInitializedForIsolate(isolate, "v8::ArrayBuffer::New(size_t)"); EnsureInitializedForIsolate(isolate, "v8::ArrayBuffer::New(size_t)");
@ -6051,7 +6069,7 @@ Local<ArrayBuffer> v8::ArrayBuffer::New(void* data, size_t byte_length) {
ENTER_V8(isolate); ENTER_V8(isolate);
i::Handle<i::JSArrayBuffer> obj = i::Handle<i::JSArrayBuffer> obj =
isolate->factory()->NewJSArrayBuffer(); isolate->factory()->NewJSArrayBuffer();
i::Runtime::SetupArrayBuffer(isolate, obj, data, byte_length); i::Runtime::SetupArrayBuffer(isolate, obj, true, data, byte_length);
return Utils::ToLocal(obj); return Utils::ToLocal(obj);
} }

View File

@ -1320,10 +1320,12 @@ void Genesis::InitializeExperimentalGlobal() {
if (FLAG_harmony_array_buffer) { if (FLAG_harmony_array_buffer) {
// -- A r r a y B u f f e r // -- A r r a y B u f f e r
Handle<JSFunction> array_buffer_fun = Handle<JSFunction> array_buffer_fun =
InstallFunction(global, "ArrayBuffer", JS_ARRAY_BUFFER_TYPE, InstallFunction(
JSArrayBuffer::kSize, global, "ArrayBuffer", JS_ARRAY_BUFFER_TYPE,
isolate()->initial_object_prototype(), JSArrayBuffer::kSize +
Builtins::kIllegal, true, true); v8::ArrayBuffer::kInternalFieldCount * kPointerSize,
isolate()->initial_object_prototype(),
Builtins::kIllegal, true, true);
native_context()->set_array_buffer_fun(*array_buffer_fun); native_context()->set_array_buffer_fun(*array_buffer_fun);
} }

View File

@ -1048,6 +1048,16 @@ static char* ReadChars(Isolate* isolate, const char* name, int* size_out) {
return chars; 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) { Handle<Value> Shell::ReadBuffer(const Arguments& args) {
ASSERT(sizeof(char) == sizeof(uint8_t)); // NOLINT ASSERT(sizeof(char) == sizeof(uint8_t)); // NOLINT
@ -1057,14 +1067,19 @@ Handle<Value> Shell::ReadBuffer(const Arguments& args) {
return Throw("Error loading file"); return Throw("Error loading file");
} }
Isolate* isolate = args.GetIsolate();
uint8_t* data = reinterpret_cast<uint8_t*>( uint8_t* data = reinterpret_cast<uint8_t*>(
ReadChars(args.GetIsolate(), *filename, &length)); ReadChars(args.GetIsolate(), *filename, &length));
if (data == NULL) { if (data == NULL) {
return Throw("Error reading file"); return Throw("Error reading file");
} }
Handle<v8::ArrayBuffer> buffer = ArrayBuffer::New(length); Handle<v8::ArrayBuffer> buffer = ArrayBuffer::New(data, length);
memcpy(buffer->Data(), data, length); v8::Persistent<v8::Value> weak_handle =
delete[] data; v8::Persistent<v8::Value>::New(isolate, buffer);
weak_handle.MakeWeak(isolate, data, ReadBufferWeakCallback);
weak_handle.MarkIndependent();
isolate->AdjustAmountOfExternalAllocatedMemory(length);
return buffer; return buffer;
} }

View File

@ -5313,6 +5313,17 @@ void JSArrayBuffer::set_backing_store(void* value, WriteBarrierMode mode) {
ACCESSORS(JSArrayBuffer, byte_length, Object, kByteLengthOffset) 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) ACCESSORS(JSTypedArray, buffer, Object, kBufferOffset)
@ -5320,7 +5331,6 @@ ACCESSORS(JSTypedArray, byte_offset, Object, kByteOffsetOffset)
ACCESSORS(JSTypedArray, byte_length, Object, kByteLengthOffset) ACCESSORS(JSTypedArray, byte_length, Object, kByteLengthOffset)
ACCESSORS(JSTypedArray, length, Object, kLengthOffset) ACCESSORS(JSTypedArray, length, Object, kLengthOffset)
ACCESSORS(JSRegExp, data, Object, kDataOffset) ACCESSORS(JSRegExp, data, Object, kDataOffset)

View File

@ -8763,6 +8763,12 @@ class JSArrayBuffer: public JSObject {
// [byte_length]: length in bytes // [byte_length]: length in bytes
DECL_ACCESSORS(byte_length, Object) DECL_ACCESSORS(byte_length, Object)
// [flags]
DECL_ACCESSORS(flag, Smi)
inline bool is_external();
inline void set_is_external(bool value);
// Casting. // Casting.
static inline JSArrayBuffer* cast(Object* obj); static inline JSArrayBuffer* cast(Object* obj);
@ -8772,9 +8778,13 @@ class JSArrayBuffer: public JSObject {
static const int kBackingStoreOffset = JSObject::kHeaderSize; static const int kBackingStoreOffset = JSObject::kHeaderSize;
static const int kByteLengthOffset = kBackingStoreOffset + kPointerSize; 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: private:
// Bit position in a flag
static const int kIsExternalBit = 0;
DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBuffer); DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBuffer);
}; };

View File

@ -658,28 +658,37 @@ static void ArrayBufferWeakCallback(v8::Isolate* external_isolate,
Isolate* isolate = reinterpret_cast<Isolate*>(external_isolate); Isolate* isolate = reinterpret_cast<Isolate*>(external_isolate);
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<Object> internal_object = Utils::OpenHandle(**object); Handle<Object> internal_object = Utils::OpenHandle(**object);
Handle<JSArrayBuffer> array_buffer(JSArrayBuffer::cast(*internal_object));
size_t allocated_length = NumberToSize( if (!array_buffer->is_external()) {
isolate, JSArrayBuffer::cast(*internal_object)->byte_length()); size_t allocated_length = NumberToSize(
isolate->heap()->AdjustAmountOfExternalAllocatedMemory( isolate, array_buffer->byte_length());
-static_cast<intptr_t>(allocated_length)); isolate->heap()->AdjustAmountOfExternalAllocatedMemory(
if (data != NULL) -static_cast<intptr_t>(allocated_length));
free(data); free(data);
}
object->Dispose(external_isolate); object->Dispose(external_isolate);
} }
bool Runtime::SetupArrayBuffer(Isolate* isolate, void Runtime::SetupArrayBuffer(Isolate* isolate,
Handle<JSArrayBuffer> array_buffer, Handle<JSArrayBuffer> array_buffer,
bool is_external,
void* data, void* data,
size_t allocated_length) { 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_backing_store(data);
array_buffer->set_flag(Smi::FromInt(0));
array_buffer->set_is_external(is_external);
Handle<Object> byte_length = Handle<Object> byte_length =
isolate->factory()->NewNumberFromSize(allocated_length); isolate->factory()->NewNumberFromSize(allocated_length);
CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber()); CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
array_buffer->set_byte_length(*byte_length); array_buffer->set_byte_length(*byte_length);
return true;
} }
@ -696,8 +705,7 @@ bool Runtime::SetupArrayBufferAllocatingData(
data = NULL; data = NULL;
} }
if (!SetupArrayBuffer(isolate, array_buffer, data, allocated_length)) SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length);
return false;
v8::Isolate* external_isolate = reinterpret_cast<v8::Isolate*>(isolate); v8::Isolate* external_isolate = reinterpret_cast<v8::Isolate*>(isolate);
v8::Persistent<v8::Value> weak_handle = v8::Persistent<v8::Value>::New( v8::Persistent<v8::Value> weak_handle = v8::Persistent<v8::Value>::New(

View File

@ -754,8 +754,9 @@ class Runtime : public AllStatic {
Handle<Object> object, Handle<Object> object,
Handle<Object> key); Handle<Object> key);
static bool SetupArrayBuffer(Isolate* isolate, static void SetupArrayBuffer(Isolate* isolate,
Handle<JSArrayBuffer> array_buffer, Handle<JSArrayBuffer> array_buffer,
bool is_external,
void* data, void* data,
size_t allocated_length); size_t allocated_length);

View File

@ -2493,7 +2493,7 @@ THREADED_TEST(SymbolProperties) {
} }
THREADED_TEST(ArrayBuffer) { THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
i::FLAG_harmony_array_buffer = true; i::FLAG_harmony_array_buffer = true;
i::FLAG_harmony_typed_arrays = true; i::FLAG_harmony_typed_arrays = true;
@ -2502,10 +2502,16 @@ THREADED_TEST(ArrayBuffer) {
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(1024); Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(1024);
CHECK_EQ(1024, ab->ByteLength());
CHECK(!ab->IsExternal());
HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); v8::ArrayBufferContents ab_contents;
uint8_t* data = static_cast<uint8_t*>(ab->Data()); 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); ASSERT(data != NULL);
env->Global()->Set(v8_str("ab"), ab); env->Global()->Set(v8_str("ab"), ab);
@ -2523,27 +2529,73 @@ THREADED_TEST(ArrayBuffer) {
data[1] = 0x11; data[1] = 0x11;
result = CompileRun("u8[0] + u8[1]"); result = CompileRun("u8[0] + u8[1]");
CHECK_EQ(0xDD, result->Int32Value()); CHECK_EQ(0xDD, result->Int32Value());
}
result = CompileRun("var ab1 = new ArrayBuffer(2);"
"var u8_a = new Uint8Array(ab1);" THREADED_TEST(ArrayBuffer_JSInternalToExternal) {
"u8_a[0] = 0xAA;" i::FLAG_harmony_array_buffer = true;
"u8_a[1] = 0xFF; u8_a.buffer"); 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); Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::Cast(*result);
CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
uint8_t* ab1_data = static_cast<uint8_t*>(ab1->Data()); CHECK(!ab1->IsExternal());
CHECK_EQ(0xAA, ab1_data[0]); 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]); CHECK_EQ(0xFF, ab1_data[1]);
ab1_data[0] = 0xCC; ab1_data[0] = 0xCC;
ab1_data[1] = 0x11; ab1_data[1] = 0x11;
result = CompileRun("u8_a[0] + u8_a[1]"); result = CompileRun("u8_a[0] + u8_a[1]");
CHECK_EQ(0xDD, result->Int32Value()); CHECK_EQ(0xDD, result->Int32Value());
}
uint8_t* my_data = new uint8_t[100];
memset(my_data, 0, 100); THREADED_TEST(ArrayBuffer_External) {
Local<v8::ArrayBuffer> ab3 = v8::ArrayBuffer::New(my_data, 100); 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(100, static_cast<int>(ab3->ByteLength()));
CHECK_EQ(my_data, ab3->Data()); CHECK(ab3->IsExternal());
env->Global()->Set(v8_str("ab3"), ab3); 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);" result = CompileRun("var u8_b = new Uint8Array(ab3);"
"u8_b[0] = 0xBB;" "u8_b[0] = 0xBB;"
"u8_b[1] = 0xCC;" "u8_b[1] = 0xCC;"
@ -2555,14 +2607,9 @@ THREADED_TEST(ArrayBuffer) {
my_data[1] = 0x11; my_data[1] = 0x11;
result = CompileRun("u8_b[0] + u8_b[1]"); result = CompileRun("u8_b[0] + u8_b[1]");
CHECK_EQ(0xDD, result->Int32Value()); CHECK_EQ(0xDD, result->Int32Value());
delete[] my_data;
} }
THREADED_TEST(HiddenProperties) { THREADED_TEST(HiddenProperties) {
LocalContext env; LocalContext env;
v8::HandleScope scope(env->GetIsolate()); v8::HandleScope scope(env->GetIsolate());
@ -15460,12 +15507,14 @@ void TypedArrayTestHelper(v8::ExternalArrayType array_type,
i::FLAG_harmony_array_buffer = true; i::FLAG_harmony_array_buffer = true;
i::FLAG_harmony_typed_arrays = true; i::FLAG_harmony_typed_arrays = true;
i::ScopedVector<ElementType> backing_store(kElementCount+2);
LocalContext env; LocalContext env;
v8::Isolate* isolate = env->GetIsolate(); v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
Local<v8::ArrayBuffer> ab = Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
v8::ArrayBuffer::New((kElementCount+2)*sizeof(ElementType)); backing_store.start(), (kElementCount+2)*sizeof(ElementType));
Local<TypedArray> ta = Local<TypedArray> ta =
TypedArray::New(ab, 2*sizeof(ElementType), kElementCount); TypedArray::New(ab, 2*sizeof(ElementType), kElementCount);
CHECK_EQ(kElementCount, static_cast<int>(ta->Length())); CHECK_EQ(kElementCount, static_cast<int>(ta->Length()));
@ -15474,7 +15523,7 @@ void TypedArrayTestHelper(v8::ExternalArrayType array_type,
static_cast<int>(ta->ByteLength())); static_cast<int>(ta->ByteLength()));
CHECK_EQ(ab, ta->Buffer()); 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++) { for (int i = 0; i < kElementCount; i++) {
data[i] = static_cast<ElementType>(i); data[i] = static_cast<ElementType>(i);
} }