Home | History | Annotate | Download | only in gin
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "gin/array_buffer.h"
      6 
      7 #include <stdlib.h>
      8 
      9 namespace gin {
     10 
     11 COMPILE_ASSERT(V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT == 2,
     12                array_buffers_must_have_two_internal_fields);
     13 
     14 static const int kBufferViewPrivateIndex = 0;
     15 
     16 // ArrayBufferAllocator -------------------------------------------------------
     17 
     18 void* ArrayBufferAllocator::Allocate(size_t length) {
     19   return calloc(1, length);
     20 }
     21 
     22 void* ArrayBufferAllocator::AllocateUninitialized(size_t length) {
     23   return malloc(length);
     24 }
     25 
     26 void ArrayBufferAllocator::Free(void* data, size_t length) {
     27   free(data);
     28 }
     29 
     30 ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() {
     31   static ArrayBufferAllocator* instance = new ArrayBufferAllocator();
     32   return instance;
     33 }
     34 
     35 // ArrayBuffer::Private -------------------------------------------------------
     36 
     37 // This class exists to solve a tricky lifetime problem. The V8 API doesn't
     38 // want to expose a direct view into the memory behind an array buffer because
     39 // V8 might deallocate that memory during garbage collection. Instead, the V8
     40 // API forces us to externalize the buffer and take ownership of the memory.
     41 // In order to know when to free the memory, we need to figure out both when
     42 // we're done with it and when V8 is done with it.
     43 //
     44 // To determine whether we're done with the memory, every view we have into
     45 // the array buffer takes a reference to the ArrayBuffer::Private object that
     46 // actually owns the memory. To determine when V8 is done with the memory, we
     47 // open a weak handle to the ArrayBuffer object. When we receive the weak
     48 // callback, we know the object is about to be garbage collected and we can
     49 // drop V8's implied reference to the memory.
     50 //
     51 // The final subtlety is that we need every ArrayBuffer into the same array
     52 // buffer to AddRef the same ArrayBuffer::Private. To make that work, we store
     53 // a pointer to the ArrayBuffer::Private object in an internal field of the
     54 // ArrayBuffer object.
     55 //
     56 class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> {
     57  public:
     58   static scoped_refptr<Private> From(v8::Isolate* isolate,
     59                                      v8::Handle<v8::ArrayBuffer> array);
     60 
     61   void* buffer() const { return buffer_; }
     62   size_t length() const { return length_; }
     63 
     64  private:
     65   friend class base::RefCounted<Private>;
     66 
     67   Private(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array);
     68   ~Private();
     69 
     70   static void WeakCallback(
     71       const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data);
     72 
     73   v8::Persistent<v8::ArrayBuffer> array_buffer_;
     74   scoped_refptr<Private> self_reference_;
     75   void* buffer_;
     76   size_t length_;
     77 };
     78 
     79 scoped_refptr<ArrayBuffer::Private> ArrayBuffer::Private::From(
     80     v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array) {
     81   if (array->IsExternal()) {
     82     return make_scoped_refptr(static_cast<Private*>(
     83         array->GetAlignedPointerFromInternalField(kBufferViewPrivateIndex)));
     84   }
     85   return make_scoped_refptr(new Private(isolate, array));
     86 }
     87 
     88 ArrayBuffer::Private::Private(v8::Isolate* isolate,
     89                               v8::Handle<v8::ArrayBuffer> array)
     90     : array_buffer_(isolate, array) {
     91   // Take ownership of the array buffer.
     92   v8::ArrayBuffer::Contents contents = array->Externalize();
     93   buffer_ = contents.Data();
     94   length_ = contents.ByteLength();
     95 
     96   array->SetAlignedPointerInInternalField(kBufferViewPrivateIndex, this);
     97 
     98   self_reference_ = this;  // Cleared in WeakCallback.
     99   array_buffer_.SetWeak(this, WeakCallback);
    100 }
    101 
    102 ArrayBuffer::Private::~Private() {
    103   ArrayBufferAllocator::SharedInstance()->Free(buffer_, length_);
    104 }
    105 
    106 void ArrayBuffer::Private::WeakCallback(
    107     const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data) {
    108   Private* parameter = data.GetParameter();
    109   parameter->array_buffer_.Reset();
    110   parameter->self_reference_ = NULL;
    111 }
    112 
    113 // ArrayBuffer ----------------------------------------------------------------
    114 
    115 ArrayBuffer::ArrayBuffer()
    116     : bytes_(0),
    117       num_bytes_(0) {
    118 }
    119 
    120 ArrayBuffer::ArrayBuffer(v8::Isolate* isolate,
    121                          v8::Handle<v8::ArrayBuffer> array) {
    122   private_ = ArrayBuffer::Private::From(isolate, array);
    123   bytes_ = private_->buffer();
    124   num_bytes_ = private_->length();
    125 }
    126 
    127 ArrayBuffer::~ArrayBuffer() {
    128 }
    129 
    130 ArrayBuffer& ArrayBuffer::operator=(const ArrayBuffer& other) {
    131   private_ = other.private_;
    132   bytes_ = other.bytes_;
    133   num_bytes_ = other.num_bytes_;
    134   return *this;
    135 }
    136 
    137 // Converter<ArrayBuffer> -----------------------------------------------------
    138 
    139 bool Converter<ArrayBuffer>::FromV8(v8::Isolate* isolate,
    140                                     v8::Handle<v8::Value> val,
    141                                     ArrayBuffer* out) {
    142   if (!val->IsArrayBuffer())
    143     return false;
    144   *out = ArrayBuffer(isolate, v8::Handle<v8::ArrayBuffer>::Cast(val));
    145   return true;
    146 }
    147 
    148 // ArrayBufferView ------------------------------------------------------------
    149 
    150 ArrayBufferView::ArrayBufferView()
    151     : offset_(0),
    152       num_bytes_(0) {
    153 }
    154 
    155 ArrayBufferView::ArrayBufferView(v8::Isolate* isolate,
    156                                  v8::Handle<v8::ArrayBufferView> view)
    157     : array_buffer_(isolate, view->Buffer()),
    158       offset_(view->ByteOffset()),
    159       num_bytes_(view->ByteLength()) {
    160 }
    161 
    162 ArrayBufferView::~ArrayBufferView() {
    163 }
    164 
    165 // Converter<ArrayBufferView> -------------------------------------------------
    166 
    167 bool Converter<ArrayBufferView>::FromV8(v8::Isolate* isolate,
    168                                         v8::Handle<v8::Value> val,
    169                                         ArrayBufferView* out) {
    170   if (!val->IsArrayBufferView())
    171     return false;
    172   *out = ArrayBufferView(isolate, v8::Handle<v8::ArrayBufferView>::Cast(val));
    173   return true;
    174 }
    175 
    176 }  // namespace gin
    177