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
This commit is contained in:
parent
b90bd6987b
commit
9278a4b7b1
91
include/v8.h
91
include/v8.h
@ -2370,30 +2370,6 @@ 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
|
#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT
|
||||||
#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2
|
#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2
|
||||||
#endif
|
#endif
|
||||||
@ -2404,6 +2380,53 @@ class V8EXPORT ArrayBufferContents {
|
|||||||
*/
|
*/
|
||||||
class V8EXPORT ArrayBuffer : public Object {
|
class V8EXPORT ArrayBuffer : public Object {
|
||||||
public:
|
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.
|
* Data length in bytes.
|
||||||
*/
|
*/
|
||||||
@ -2440,14 +2463,18 @@ class V8EXPORT ArrayBuffer : public Object {
|
|||||||
void Neuter();
|
void Neuter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass the ownership of this ArrayBuffer's backing store to
|
* Make this ArrayBuffer external. The pointer to underlying memory block
|
||||||
* a given ArrayBufferContents.
|
* 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));
|
V8_INLINE(static ArrayBuffer* Cast(Value* obj));
|
||||||
|
|
||||||
|
|
||||||
static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
|
static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -4193,6 +4220,14 @@ class V8EXPORT V8 {
|
|||||||
static void SetAllowCodeGenerationFromStringsCallback(
|
static void SetAllowCodeGenerationFromStringsCallback(
|
||||||
AllowCodeGenerationFromStringsCallback that);
|
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.
|
* Ignore out-of-memory exceptions.
|
||||||
*
|
*
|
||||||
|
27
src/api.cc
27
src/api.cc
@ -5160,6 +5160,15 @@ void v8::V8::SetJitCodeEventHandler(
|
|||||||
isolate->logger()->SetCodeEventHandler(options, event_handler);
|
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() {
|
bool v8::V8::Dispose() {
|
||||||
i::Isolate* isolate = i::Isolate::Current();
|
i::Isolate* isolate = i::Isolate::Current();
|
||||||
@ -6129,25 +6138,17 @@ bool v8::ArrayBuffer::IsExternal() const {
|
|||||||
return Utils::OpenHandle(this)->is_external();
|
return Utils::OpenHandle(this)->is_external();
|
||||||
}
|
}
|
||||||
|
|
||||||
v8::ArrayBufferContents::~ArrayBufferContents() {
|
v8::ArrayBuffer::Contents v8::ArrayBuffer::Externalize() {
|
||||||
free(data_);
|
|
||||||
data_ = NULL;
|
|
||||||
byte_length_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void v8::ArrayBuffer::Externalize(ArrayBufferContents* contents) {
|
|
||||||
i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
|
i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
|
||||||
ApiCheck(!obj->is_external(),
|
ApiCheck(!obj->is_external(),
|
||||||
"v8::ArrayBuffer::Externalize",
|
"v8::ArrayBuffer::Externalize",
|
||||||
"ArrayBuffer already externalized");
|
"ArrayBuffer already externalized");
|
||||||
obj->set_is_external(true);
|
obj->set_is_external(true);
|
||||||
size_t byte_length = static_cast<size_t>(obj->byte_length()->Number());
|
size_t byte_length = static_cast<size_t>(obj->byte_length()->Number());
|
||||||
ApiCheck(contents->data_ == NULL,
|
Contents contents;
|
||||||
"v8::ArrayBuffer::Externalize",
|
contents.data_ = obj->backing_store();
|
||||||
"Externalizing into non-empty ArrayBufferContents");
|
contents.byte_length_ = byte_length;
|
||||||
contents->data_ = obj->backing_store();
|
return contents;
|
||||||
contents->byte_length_ = byte_length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1571,6 +1571,13 @@ static void EnableHarmonyTypedArraysViaCommandLine() {
|
|||||||
#endif
|
#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[]) {
|
int Shell::Main(int argc, char* argv[]) {
|
||||||
if (!SetOptions(argc, argv)) return 1;
|
if (!SetOptions(argc, argv)) return 1;
|
||||||
#ifndef V8_SHARED
|
#ifndef V8_SHARED
|
||||||
@ -1579,6 +1586,8 @@ int Shell::Main(int argc, char* argv[]) {
|
|||||||
#else
|
#else
|
||||||
EnableHarmonyTypedArraysViaCommandLine();
|
EnableHarmonyTypedArraysViaCommandLine();
|
||||||
#endif
|
#endif
|
||||||
|
ShellArrayBufferAllocator array_buffer_allocator;
|
||||||
|
v8::V8::SetArrayBufferAllocator(&array_buffer_allocator);
|
||||||
int result = 0;
|
int result = 0;
|
||||||
Isolate* isolate = Isolate::GetCurrent();
|
Isolate* isolate = Isolate::GetCurrent();
|
||||||
DumbLineEditor dumb_line_editor(isolate);
|
DumbLineEditor dumb_line_editor(isolate);
|
||||||
|
@ -663,7 +663,8 @@ static void ArrayBufferWeakCallback(v8::Isolate* external_isolate,
|
|||||||
isolate, array_buffer->byte_length());
|
isolate, array_buffer->byte_length());
|
||||||
isolate->heap()->AdjustAmountOfExternalAllocatedMemory(
|
isolate->heap()->AdjustAmountOfExternalAllocatedMemory(
|
||||||
-static_cast<intptr_t>(allocated_length));
|
-static_cast<intptr_t>(allocated_length));
|
||||||
free(data);
|
CHECK(V8::ArrayBufferAllocator() != NULL);
|
||||||
|
V8::ArrayBufferAllocator()->Free(data);
|
||||||
}
|
}
|
||||||
object->Dispose(external_isolate);
|
object->Dispose(external_isolate);
|
||||||
}
|
}
|
||||||
@ -699,8 +700,9 @@ bool Runtime::SetupArrayBufferAllocatingData(
|
|||||||
Handle<JSArrayBuffer> array_buffer,
|
Handle<JSArrayBuffer> array_buffer,
|
||||||
size_t allocated_length) {
|
size_t allocated_length) {
|
||||||
void* data;
|
void* data;
|
||||||
|
CHECK(V8::ArrayBufferAllocator() != NULL);
|
||||||
if (allocated_length != 0) {
|
if (allocated_length != 0) {
|
||||||
data = malloc(allocated_length);
|
data = V8::ArrayBufferAllocator()->Allocate(allocated_length);
|
||||||
if (data == NULL) return false;
|
if (data == NULL) return false;
|
||||||
memset(data, 0, allocated_length);
|
memset(data, 0, allocated_length);
|
||||||
} else {
|
} else {
|
||||||
|
@ -56,6 +56,7 @@ bool V8::has_been_disposed_ = false;
|
|||||||
bool V8::has_fatal_error_ = false;
|
bool V8::has_fatal_error_ = false;
|
||||||
bool V8::use_crankshaft_ = true;
|
bool V8::use_crankshaft_ = true;
|
||||||
List<CallCompletedCallback>* V8::call_completed_callbacks_ = NULL;
|
List<CallCompletedCallback>* V8::call_completed_callbacks_ = NULL;
|
||||||
|
v8::ArrayBuffer::Allocator* V8::array_buffer_allocator_ = NULL;
|
||||||
|
|
||||||
static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER;
|
static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
11
src/v8.h
11
src/v8.h
@ -121,6 +121,15 @@ class V8 : public AllStatic {
|
|||||||
static void RemoveCallCompletedCallback(CallCompletedCallback callback);
|
static void RemoveCallCompletedCallback(CallCompletedCallback callback);
|
||||||
static void FireCallCompletedCallback(Isolate* isolate);
|
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:
|
private:
|
||||||
static void InitializeOncePerProcessImpl();
|
static void InitializeOncePerProcessImpl();
|
||||||
static void InitializeOncePerProcess();
|
static void InitializeOncePerProcess();
|
||||||
@ -139,6 +148,8 @@ class V8 : public AllStatic {
|
|||||||
static bool use_crankshaft_;
|
static bool use_crankshaft_;
|
||||||
// List of callbacks when a Call completes.
|
// List of callbacks when a Call completes.
|
||||||
static List<CallCompletedCallback>* call_completed_callbacks_;
|
static List<CallCompletedCallback>* call_completed_callbacks_;
|
||||||
|
// Allocator for external array buffers.
|
||||||
|
static v8::ArrayBuffer::Allocator* array_buffer_allocator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,10 +98,21 @@ static void PrintTestList(CcTest* current) {
|
|||||||
v8::Isolate* CcTest::default_isolate_;
|
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[]) {
|
int main(int argc, char* argv[]) {
|
||||||
v8::internal::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
|
v8::internal::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
|
||||||
v8::internal::FLAG_harmony_array_buffer = true;
|
v8::internal::FLAG_harmony_array_buffer = true;
|
||||||
v8::internal::FLAG_harmony_typed_arrays = 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());
|
CcTest::set_default_isolate(v8::Isolate::GetCurrent());
|
||||||
CHECK(CcTest::default_isolate() != NULL);
|
CHECK(CcTest::default_isolate() != NULL);
|
||||||
int tests_run = 0;
|
int tests_run = 0;
|
||||||
|
@ -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) {
|
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;
|
||||||
@ -2583,8 +2596,7 @@ THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
|
|||||||
CHECK(!ab->IsExternal());
|
CHECK(!ab->IsExternal());
|
||||||
HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
|
HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
|
||||||
|
|
||||||
v8::ArrayBufferContents ab_contents;
|
ScopedArrayBufferContents ab_contents(ab->Externalize());
|
||||||
ab->Externalize(&ab_contents);
|
|
||||||
CHECK(ab->IsExternal());
|
CHECK(ab->IsExternal());
|
||||||
|
|
||||||
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
|
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
|
||||||
@ -2626,8 +2638,7 @@ THREADED_TEST(ArrayBuffer_JSInternalToExternal) {
|
|||||||
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()));
|
||||||
CHECK(!ab1->IsExternal());
|
CHECK(!ab1->IsExternal());
|
||||||
v8::ArrayBufferContents ab1_contents;
|
ScopedArrayBufferContents ab1_contents(ab1->Externalize());
|
||||||
ab1->Externalize(&ab1_contents);
|
|
||||||
CHECK(ab1->IsExternal());
|
CHECK(ab1->IsExternal());
|
||||||
|
|
||||||
result = CompileRun("ab1.byteLength");
|
result = CompileRun("ab1.byteLength");
|
||||||
@ -2734,8 +2745,7 @@ THREADED_TEST(ArrayBuffer_NeuteringApi) {
|
|||||||
v8::Handle<v8::Float64Array> f64a =
|
v8::Handle<v8::Float64Array> f64a =
|
||||||
CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127);
|
CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127);
|
||||||
|
|
||||||
v8::ArrayBufferContents contents;
|
ScopedArrayBufferContents contents(buffer->Externalize());
|
||||||
buffer->Externalize(&contents);
|
|
||||||
buffer->Neuter();
|
buffer->Neuter();
|
||||||
CHECK_EQ(0, static_cast<int>(buffer->ByteLength()));
|
CHECK_EQ(0, static_cast<int>(buffer->ByteLength()));
|
||||||
CheckIsNeutered(u8a);
|
CheckIsNeutered(u8a);
|
||||||
@ -2786,8 +2796,7 @@ THREADED_TEST(ArrayBuffer_NeuteringScript) {
|
|||||||
v8::Handle<v8::Float64Array> f64a(
|
v8::Handle<v8::Float64Array> f64a(
|
||||||
v8::Float64Array::Cast(*CompileRun("f64a")));
|
v8::Float64Array::Cast(*CompileRun("f64a")));
|
||||||
|
|
||||||
v8::ArrayBufferContents contents;
|
ScopedArrayBufferContents contents(ab->Externalize());
|
||||||
ab->Externalize(&contents);
|
|
||||||
ab->Neuter();
|
ab->Neuter();
|
||||||
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
|
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
|
||||||
CheckIsNeutered(u8a);
|
CheckIsNeutered(u8a);
|
||||||
|
Loading…
Reference in New Issue
Block a user