From: dslomov@chromium.org Date: Tue, 11 Jun 2013 10:41:14 +0000 (+0000) Subject: Change ArrayBuffer API and implementation to use embedder-provided allocator. X-Git-Tag: upstream/4.7.83~13915 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9278a4b7b1e22dea6c6a6b6e06ea264946c86c45;p=platform%2Fupstream%2Fv8.git Change ArrayBuffer API and implementation to use embedder-provided allocator. R=svenpanne@chromium.org Review URL: https://codereview.chromium.org/15855012 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15056 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/include/v8.h b/include/v8.h index b7eb294..c0bec79 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2370,30 +2370,6 @@ class V8EXPORT Function : public Object { 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 @@ -2405,6 +2381,53 @@ class V8EXPORT ArrayBufferContents { class V8EXPORT ArrayBuffer : public Object { public: /** + * Allocator that V8 uses to allocate |ArrayBuffer|'s memory. + * The allocator is a global V8 setting. It should be set with + * V8::SetArrayBufferAllocator prior to creation of a first ArrayBuffer. + * + * This API is experimental and may change significantly. + */ + class V8EXPORT Allocator { // NOLINT + public: + virtual ~Allocator() {} + + /** + * Allocate |length| bytes. Return NULL if allocation is not successful. + */ + virtual void* Allocate(size_t length) = 0; + /** + * Free the memory pointed to |data|. That memory is guaranteed to be + * previously allocated by |Allocate|. + */ + virtual void Free(void* data) = 0; + }; + + /** + * The contents of an |ArrayBuffer|. Externalization of |ArrayBuffer| + * returns an instance of this class, populated, with a pointer to data + * and byte length. + * + * The Data pointer of ArrayBuffer::Contents is always allocated with + * Allocator::Allocate that is set with V8::SetArrayBufferAllocator. + * + * This API is experimental and may change significantly. + */ + class V8EXPORT Contents { // NOLINT + public: + Contents() : data_(NULL), byte_length_(0) {} + + void* Data() const { return data_; } + size_t ByteLength() const { return byte_length_; } + + private: + void* data_; + size_t byte_length_; + + friend class ArrayBuffer; + }; + + + /** * Data length in bytes. */ size_t ByteLength() const; @@ -2440,14 +2463,18 @@ class V8EXPORT ArrayBuffer : public Object { void Neuter(); /** - * Pass the ownership of this ArrayBuffer's backing store to - * a given ArrayBufferContents. + * Make this ArrayBuffer external. The pointer to underlying memory block + * and byte length are returned as |Contents| structure. After ArrayBuffer + * had been etxrenalized, it does no longer owns the memory block. The caller + * should take steps to free memory when it is no longer needed. + * + * The memory block is guaranteed to be allocated with |Allocator::Allocate| + * that has been set with V8::SetArrayBufferAllocator. */ - void Externalize(ArrayBufferContents* contents); + Contents Externalize(); V8_INLINE(static ArrayBuffer* Cast(Value* obj)); - static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; private: @@ -4194,6 +4221,14 @@ class V8EXPORT V8 { AllowCodeGenerationFromStringsCallback that); /** + * Set allocator to use for ArrayBuffer memory. + * The allocator should be set only once. The allocator should be set + * before any code tha uses ArrayBuffers is executed. + * This allocator is used in all isolates. + */ + static void SetArrayBufferAllocator(ArrayBuffer::Allocator* allocator); + + /** * Ignore out-of-memory exceptions. * * V8 running out of memory is treated as a fatal error by default. diff --git a/src/api.cc b/src/api.cc index 18acc0d..20496fe 100644 --- a/src/api.cc +++ b/src/api.cc @@ -5160,6 +5160,15 @@ void v8::V8::SetJitCodeEventHandler( isolate->logger()->SetCodeEventHandler(options, event_handler); } +void v8::V8::SetArrayBufferAllocator( + ArrayBuffer::Allocator* allocator) { + if (!ApiCheck(i::V8::ArrayBufferAllocator() == NULL, + "v8::V8::SetArrayBufferAllocator", + "ArrayBufferAllocator might only be set once")) + return; + i::V8::SetArrayBufferAllocator(allocator); +} + bool v8::V8::Dispose() { i::Isolate* isolate = i::Isolate::Current(); @@ -6129,25 +6138,17 @@ 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) { +v8::ArrayBuffer::Contents v8::ArrayBuffer::Externalize() { i::Handle 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(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; + Contents contents; + contents.data_ = obj->backing_store(); + contents.byte_length_ = byte_length; + return contents; } diff --git a/src/d8.cc b/src/d8.cc index 57e0c04..a917dbd 100644 --- a/src/d8.cc +++ b/src/d8.cc @@ -1571,6 +1571,13 @@ static void EnableHarmonyTypedArraysViaCommandLine() { #endif +class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator { + public: + virtual void* Allocate(size_t length) { return malloc(length); } + virtual void Free(void* data) { free(data); } +}; + + int Shell::Main(int argc, char* argv[]) { if (!SetOptions(argc, argv)) return 1; #ifndef V8_SHARED @@ -1579,6 +1586,8 @@ int Shell::Main(int argc, char* argv[]) { #else EnableHarmonyTypedArraysViaCommandLine(); #endif + ShellArrayBufferAllocator array_buffer_allocator; + v8::V8::SetArrayBufferAllocator(&array_buffer_allocator); int result = 0; Isolate* isolate = Isolate::GetCurrent(); DumbLineEditor dumb_line_editor(isolate); diff --git a/src/runtime.cc b/src/runtime.cc index 074d8dc..a1f465f 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -663,7 +663,8 @@ static void ArrayBufferWeakCallback(v8::Isolate* external_isolate, isolate, array_buffer->byte_length()); isolate->heap()->AdjustAmountOfExternalAllocatedMemory( -static_cast(allocated_length)); - free(data); + CHECK(V8::ArrayBufferAllocator() != NULL); + V8::ArrayBufferAllocator()->Free(data); } object->Dispose(external_isolate); } @@ -699,8 +700,9 @@ bool Runtime::SetupArrayBufferAllocatingData( Handle array_buffer, size_t allocated_length) { void* data; + CHECK(V8::ArrayBufferAllocator() != NULL); if (allocated_length != 0) { - data = malloc(allocated_length); + data = V8::ArrayBufferAllocator()->Allocate(allocated_length); if (data == NULL) return false; memset(data, 0, allocated_length); } else { diff --git a/src/v8.cc b/src/v8.cc index e21c815..80b12de 100644 --- a/src/v8.cc +++ b/src/v8.cc @@ -56,6 +56,7 @@ bool V8::has_been_disposed_ = false; bool V8::has_fatal_error_ = false; bool V8::use_crankshaft_ = true; List* V8::call_completed_callbacks_ = NULL; +v8::ArrayBuffer::Allocator* V8::array_buffer_allocator_ = NULL; static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER; diff --git a/src/v8.h b/src/v8.h index e906035..b8a5ae4 100644 --- a/src/v8.h +++ b/src/v8.h @@ -121,6 +121,15 @@ class V8 : public AllStatic { static void RemoveCallCompletedCallback(CallCompletedCallback callback); static void FireCallCompletedCallback(Isolate* isolate); + static v8::ArrayBuffer::Allocator* ArrayBufferAllocator() { + return array_buffer_allocator_; + } + + static void SetArrayBufferAllocator(v8::ArrayBuffer::Allocator *allocator) { + CHECK_EQ(NULL, array_buffer_allocator_); + array_buffer_allocator_ = allocator; + } + private: static void InitializeOncePerProcessImpl(); static void InitializeOncePerProcess(); @@ -139,6 +148,8 @@ class V8 : public AllStatic { static bool use_crankshaft_; // List of callbacks when a Call completes. static List* call_completed_callbacks_; + // Allocator for external array buffers. + static v8::ArrayBuffer::Allocator* array_buffer_allocator_; }; diff --git a/test/cctest/cctest.cc b/test/cctest/cctest.cc index b241f32..94dcce1 100644 --- a/test/cctest/cctest.cc +++ b/test/cctest/cctest.cc @@ -98,10 +98,21 @@ static void PrintTestList(CcTest* current) { v8::Isolate* CcTest::default_isolate_; +class CcTestArrayBufferAllocator : public v8::ArrayBuffer::Allocator { + public: + virtual void* Allocate(size_t length) { return malloc(length); } + virtual void Free(void* data) { free(data); } +}; + + int main(int argc, char* argv[]) { v8::internal::FlagList::SetFlagsFromCommandLine(&argc, argv, true); v8::internal::FLAG_harmony_array_buffer = true; v8::internal::FLAG_harmony_typed_arrays = true; + + CcTestArrayBufferAllocator array_buffer_allocator; + v8::V8::SetArrayBufferAllocator(&array_buffer_allocator); + CcTest::set_default_isolate(v8::Isolate::GetCurrent()); CHECK(CcTest::default_isolate() != NULL); int tests_run = 0; diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index f5e315b..5d3a79d 100755 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -2570,6 +2570,19 @@ THREADED_TEST(SymbolProperties) { } +class ScopedArrayBufferContents { + public: + explicit ScopedArrayBufferContents( + const v8::ArrayBuffer::Contents& contents) + : contents_(contents) {} + ~ScopedArrayBufferContents() { free(contents_.Data()); } + void* Data() const { return contents_.Data(); } + size_t ByteLength() const { return contents_.ByteLength(); } + private: + const v8::ArrayBuffer::Contents contents_; +}; + + THREADED_TEST(ArrayBuffer_ApiInternalToExternal) { i::FLAG_harmony_array_buffer = true; i::FLAG_harmony_typed_arrays = true; @@ -2583,8 +2596,7 @@ THREADED_TEST(ArrayBuffer_ApiInternalToExternal) { CHECK(!ab->IsExternal()); HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); - v8::ArrayBufferContents ab_contents; - ab->Externalize(&ab_contents); + ScopedArrayBufferContents ab_contents(ab->Externalize()); CHECK(ab->IsExternal()); CHECK_EQ(1024, static_cast(ab_contents.ByteLength())); @@ -2626,8 +2638,7 @@ THREADED_TEST(ArrayBuffer_JSInternalToExternal) { Local ab1 = v8::ArrayBuffer::Cast(*result); CHECK_EQ(2, static_cast(ab1->ByteLength())); CHECK(!ab1->IsExternal()); - v8::ArrayBufferContents ab1_contents; - ab1->Externalize(&ab1_contents); + ScopedArrayBufferContents ab1_contents(ab1->Externalize()); CHECK(ab1->IsExternal()); result = CompileRun("ab1.byteLength"); @@ -2734,8 +2745,7 @@ THREADED_TEST(ArrayBuffer_NeuteringApi) { v8::Handle f64a = CreateAndCheck(buffer, 8, 127); - v8::ArrayBufferContents contents; - buffer->Externalize(&contents); + ScopedArrayBufferContents contents(buffer->Externalize()); buffer->Neuter(); CHECK_EQ(0, static_cast(buffer->ByteLength())); CheckIsNeutered(u8a); @@ -2786,8 +2796,7 @@ THREADED_TEST(ArrayBuffer_NeuteringScript) { v8::Handle f64a( v8::Float64Array::Cast(*CompileRun("f64a"))); - v8::ArrayBufferContents contents; - ab->Externalize(&contents); + ScopedArrayBufferContents contents(ab->Externalize()); ab->Neuter(); CHECK_EQ(0, static_cast(ab->ByteLength())); CheckIsNeutered(u8a);