Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2010 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #ifndef ScriptWrappable_h
     32 #define ScriptWrappable_h
     33 
     34 #include "bindings/core/v8/WrapperTypeInfo.h"
     35 #include "platform/ScriptForbiddenScope.h"
     36 #include "platform/heap/Handle.h"
     37 #include <v8.h>
     38 
     39 namespace blink {
     40 
     41 /**
     42  * The base class of all wrappable objects.
     43  *
     44  * This class provides the internal pointer to be stored in the wrapper objects,
     45  * and its conversions from / to the DOM instances.
     46  *
     47  * Note that this class must not have vtbl (any virtual function) or any member
     48  * variable which increase the size of instances. Some of the classes sensitive
     49  * to the size inherit from this class. So this class must be zero size.
     50  */
     51 #if COMPILER(MSVC)
     52 // VC++ 2013 doesn't support EBCO (Empty Base Class Optimization). It causes
     53 // that not always pointers to an empty base class are aligned to 4 byte
     54 // alignment. For example,
     55 //
     56 //   class EmptyBase1 {};
     57 //   class EmptyBase2 {};
     58 //   class Derived : public EmptyBase1, public EmptyBase2 {};
     59 //   Derived d;
     60 //   // &d                           == 0x1000
     61 //   // static_cast<EmptyBase1*>(&d) == 0x1000
     62 //   // static_cast<EmptyBase2*>(&d) == 0x1001  // Not 4 byte alignment!
     63 //
     64 // This doesn't happen with other compilers which support EBCO. All the
     65 // addresses in the above example will be 0x1000 with EBCO supported.
     66 //
     67 // Since v8::Object::SetAlignedPointerInInternalField requires the pointers to
     68 // be aligned, we need a hack to specify at least 4 byte alignment to MSVC.
     69 __declspec(align(4))
     70 #endif
     71 class ScriptWrappableBase {
     72 public:
     73     template<typename T>
     74     T* toImpl()
     75     {
     76         // Check if T* is castable to ScriptWrappableBase*, which means T
     77         // doesn't have two or more ScriptWrappableBase as superclasses.
     78         // If T has two ScriptWrappableBase as superclasses, conversions
     79         // from T* to ScriptWrappableBase* are ambiguous.
     80         ASSERT(static_cast<ScriptWrappableBase*>(static_cast<T*>(this)));
     81         // The internal pointers must be aligned to at least 4 byte alignment.
     82         ASSERT((reinterpret_cast<intptr_t>(this) & 0x3) == 0);
     83         return static_cast<T*>(this);
     84     }
     85     ScriptWrappableBase* toScriptWrappableBase()
     86     {
     87         // The internal pointers must be aligned to at least 4 byte alignment.
     88         ASSERT((reinterpret_cast<intptr_t>(this) & 0x3) == 0);
     89         return this;
     90     }
     91 
     92     void assertWrapperSanity(v8::Local<v8::Object> object)
     93     {
     94         RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object.IsEmpty()
     95             || object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == toScriptWrappableBase());
     96     }
     97 };
     98 
     99 /**
    100  * ScriptWrappable wraps a V8 object and its WrapperTypeInfo.
    101  *
    102  * ScriptWrappable acts much like a v8::Persistent<> in that it keeps a
    103  * V8 object alive.
    104  *
    105  *  The state transitions are:
    106  *  - new: an empty ScriptWrappable.
    107  *  - setWrapper: install a v8::Persistent (or empty)
    108  *  - disposeWrapper (via setWeakCallback, triggered by V8 garbage collecter):
    109  *        remove v8::Persistent and become empty.
    110  */
    111 class ScriptWrappable : public ScriptWrappableBase {
    112 public:
    113     ScriptWrappable() : m_wrapper(0) { }
    114 
    115     // Returns the WrapperTypeInfo of the instance.
    116     //
    117     // This method must be overridden by DEFINE_WRAPPERTYPEINFO macro.
    118     virtual const WrapperTypeInfo* wrapperTypeInfo() const = 0;
    119 
    120     // Creates and returns a new wrapper object.
    121     virtual v8::Handle<v8::Object> wrap(v8::Handle<v8::Object> creationContext, v8::Isolate*);
    122 
    123     // Associates the instance with the existing wrapper. Returns |wrapper|.
    124     virtual v8::Handle<v8::Object> associateWithWrapper(const WrapperTypeInfo*, v8::Handle<v8::Object> wrapper, v8::Isolate*);
    125 
    126     void setWrapper(v8::Handle<v8::Object> wrapper, v8::Isolate* isolate, const WrapperTypeInfo* wrapperTypeInfo)
    127     {
    128         ASSERT(!containsWrapper());
    129         if (!*wrapper) {
    130             m_wrapper = 0;
    131             return;
    132         }
    133         v8::Persistent<v8::Object> persistent(isolate, wrapper);
    134         wrapperTypeInfo->configureWrapper(&persistent);
    135         persistent.SetWeak(this, &setWeakCallback);
    136         m_wrapper = persistent.ClearAndLeak();
    137         ASSERT(containsWrapper());
    138     }
    139 
    140     v8::Local<v8::Object> newLocalWrapper(v8::Isolate* isolate) const
    141     {
    142         v8::Persistent<v8::Object> persistent;
    143         getPersistent(&persistent);
    144         return v8::Local<v8::Object>::New(isolate, persistent);
    145     }
    146 
    147     bool isEqualTo(const v8::Local<v8::Object>& other) const
    148     {
    149         v8::Persistent<v8::Object> persistent;
    150         getPersistent(&persistent);
    151         return persistent == other;
    152     }
    153 
    154     static bool wrapperCanBeStoredInObject(const void*) { return false; }
    155     static bool wrapperCanBeStoredInObject(const ScriptWrappable*) { return true; }
    156 
    157     static ScriptWrappable* fromObject(const void*)
    158     {
    159         ASSERT_NOT_REACHED();
    160         return 0;
    161     }
    162 
    163     static ScriptWrappable* fromObject(ScriptWrappable* object)
    164     {
    165         return object;
    166     }
    167 
    168     bool setReturnValue(v8::ReturnValue<v8::Value> returnValue)
    169     {
    170         v8::Persistent<v8::Object> persistent;
    171         getPersistent(&persistent);
    172         returnValue.Set(persistent);
    173         return containsWrapper();
    174     }
    175 
    176     void markAsDependentGroup(ScriptWrappable* groupRoot, v8::Isolate* isolate)
    177     {
    178         ASSERT(containsWrapper());
    179         ASSERT(groupRoot && groupRoot->containsWrapper());
    180 
    181         v8::UniqueId groupId(reinterpret_cast<intptr_t>(groupRoot->m_wrapper));
    182         v8::Persistent<v8::Object> wrapper;
    183         getPersistent(&wrapper);
    184         wrapper.MarkPartiallyDependent();
    185         isolate->SetObjectGroupId(v8::Persistent<v8::Value>::Cast(wrapper), groupId);
    186     }
    187 
    188     void setReference(const v8::Persistent<v8::Object>& parent, v8::Isolate* isolate)
    189     {
    190         v8::Persistent<v8::Object> persistent;
    191         getPersistent(&persistent);
    192         isolate->SetReference(parent, persistent);
    193     }
    194 
    195     template<typename V8T, typename T>
    196     static void assertWrapperSanity(v8::Local<v8::Object> object, T* objectAsT)
    197     {
    198         ASSERT(objectAsT);
    199         RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object.IsEmpty()
    200             || object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == V8T::toScriptWrappableBase(objectAsT));
    201     }
    202 
    203     template<typename V8T, typename T>
    204     static void assertWrapperSanity(void* object, T* objectAsT)
    205     {
    206         ASSERT_NOT_REACHED();
    207     }
    208 
    209     template<typename V8T, typename T>
    210     static void assertWrapperSanity(ScriptWrappable* object, T* objectAsT)
    211     {
    212         ASSERT(object);
    213         ASSERT(objectAsT);
    214         v8::Object* value = object->m_wrapper;
    215         RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(value == 0
    216             || value->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == V8T::toScriptWrappableBase(objectAsT));
    217     }
    218 
    219     using ScriptWrappableBase::assertWrapperSanity;
    220 
    221     bool containsWrapper() const { return m_wrapper; }
    222 
    223 #if !ENABLE(OILPAN)
    224 protected:
    225     virtual ~ScriptWrappable()
    226     {
    227         // We must not get deleted as long as we contain a wrapper. If this happens, we screwed up ref
    228         // counting somewhere. Crash here instead of crashing during a later gc cycle.
    229         RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper());
    230         m_wrapper = 0; // Break UAF attempts to wrap.
    231     }
    232 #endif
    233     // With Oilpan we don't need a ScriptWrappable destructor.
    234     //
    235     // - 'RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper())' is not needed
    236     // because Oilpan is not using reference counting at all. If containsWrapper() is true,
    237     // it means that ScriptWrappable still has a wrapper. In this case, the destructor
    238     // must not be called since the wrapper has a persistent handle back to this ScriptWrappable object.
    239     // Assuming that Oilpan's GC is correct (If we cannot assume this, a lot of more things are
    240     // already broken), we must not hit the RELEASE_ASSERT.
    241     //
    242     // - 'm_wrapper = 0' is not needed because Oilpan's GC zeroes out memory when
    243     // the memory is collected and added to a free list.
    244 
    245 private:
    246     void getPersistent(v8::Persistent<v8::Object>* persistent) const
    247     {
    248         ASSERT(persistent);
    249 
    250         // Horrible and super unsafe: Cast the Persistent to an Object*, so
    251         // that we can inject the wrapped value. This only works because
    252         // we previously 'stole' the object pointer from a Persistent in
    253         // the setWrapper() method.
    254         *reinterpret_cast<v8::Object**>(persistent) = m_wrapper;
    255     }
    256 
    257     void disposeWrapper(v8::Local<v8::Object> wrapper)
    258     {
    259         ASSERT(containsWrapper());
    260 
    261         v8::Persistent<v8::Object> persistent;
    262         getPersistent(&persistent);
    263 
    264         ASSERT(wrapper == persistent);
    265         persistent.Reset();
    266         m_wrapper = 0;
    267     }
    268 
    269     static void setWeakCallback(const v8::WeakCallbackData<v8::Object, ScriptWrappable>& data)
    270     {
    271         v8::Persistent<v8::Object> persistent;
    272         data.GetParameter()->getPersistent(&persistent);
    273         ASSERT(persistent == data.GetValue());
    274         data.GetParameter()->disposeWrapper(data.GetValue());
    275 
    276         // FIXME: I noticed that 50%~ of minor GC cycle times can be consumed
    277         // inside data.GetParameter()->deref(), which causes Node destructions. We should
    278         // make Node destructions incremental.
    279         releaseObject(data.GetValue());
    280     }
    281 
    282     v8::Object* m_wrapper;
    283 };
    284 
    285 // Defines 'wrapperTypeInfo' virtual method which returns the WrapperTypeInfo of
    286 // the instance. Also declares a static member of type WrapperTypeInfo, of which
    287 // the definition is given by the IDL code generator.
    288 //
    289 // Every DOM Class T must meet either of the following conditions:
    290 // - T.idl inherits from [NotScriptWrappable].
    291 // - T inherits from ScriptWrappable and has DEFINE_WRAPPERTYPEINFO().
    292 //
    293 // If a DOM class T does not inherit from ScriptWrappable, you have to write
    294 // [NotScriptWrappable] in the IDL file as an extended attribute in order to let
    295 // IDL code generator know that T does not inherit from ScriptWrappable. Note
    296 // that [NotScriptWrappable] is inheritable.
    297 //
    298 // All the derived classes of ScriptWrappable, regardless of directly or
    299 // indirectly, must write this macro in the class definition.
    300 #define DEFINE_WRAPPERTYPEINFO() \
    301 public: \
    302     virtual const WrapperTypeInfo* wrapperTypeInfo() const OVERRIDE \
    303     { \
    304         return &s_wrapperTypeInfo; \
    305     } \
    306 private: \
    307     static const WrapperTypeInfo& s_wrapperTypeInfo
    308 
    309 } // namespace blink
    310 
    311 #endif // ScriptWrappable_h
    312