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
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;
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:
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.
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();
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<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;
+ Contents contents;
+ contents.data_ = obj->backing_store();
+ contents.byte_length_ = byte_length;
+ return contents;
}
#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
#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);
isolate, array_buffer->byte_length());
isolate->heap()->AdjustAmountOfExternalAllocatedMemory(
-static_cast<intptr_t>(allocated_length));
- free(data);
+ CHECK(V8::ArrayBufferAllocator() != NULL);
+ V8::ArrayBufferAllocator()->Free(data);
}
object->Dispose(external_isolate);
}
Handle<JSArrayBuffer> 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 {
bool V8::has_fatal_error_ = false;
bool V8::use_crankshaft_ = true;
List<CallCompletedCallback>* V8::call_completed_callbacks_ = NULL;
+v8::ArrayBuffer::Allocator* V8::array_buffer_allocator_ = NULL;
static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER;
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();
static bool use_crankshaft_;
// List of callbacks when a Call completes.
static List<CallCompletedCallback>* call_completed_callbacks_;
+ // Allocator for external array buffers.
+ static v8::ArrayBuffer::Allocator* array_buffer_allocator_;
};
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;
}
+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;
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<int>(ab_contents.ByteLength()));
Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::Cast(*result);
CHECK_EQ(2, static_cast<int>(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");
v8::Handle<v8::Float64Array> f64a =
CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127);
- v8::ArrayBufferContents contents;
- buffer->Externalize(&contents);
+ ScopedArrayBufferContents contents(buffer->Externalize());
buffer->Neuter();
CHECK_EQ(0, static_cast<int>(buffer->ByteLength()));
CheckIsNeutered(u8a);
v8::Handle<v8::Float64Array> f64a(
v8::Float64Array::Cast(*CompileRun("f64a")));
- v8::ArrayBufferContents contents;
- ab->Externalize(&contents);
+ ScopedArrayBufferContents contents(ab->Externalize());
ab->Neuter();
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
CheckIsNeutered(u8a);