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> toImpl()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 } toScriptWrappableBase()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 assertWrapperSanity(v8::Local<v8::Object> object)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: ScriptWrappable()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 setWrapper(v8::Handle<v8::Object> wrapper,v8::Isolate * isolate,const WrapperTypeInfo * wrapperTypeInfo)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 newLocalWrapper(v8::Isolate * isolate)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 isEqualTo(const v8::Local<v8::Object> & other)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 wrapperCanBeStoredInObject(const void *)154 static bool wrapperCanBeStoredInObject(const void*) { return false; } wrapperCanBeStoredInObject(const ScriptWrappable *)155 static bool wrapperCanBeStoredInObject(const ScriptWrappable*) { return true; } 156 fromObject(const void *)157 static ScriptWrappable* fromObject(const void*) 158 { 159 ASSERT_NOT_REACHED(); 160 return 0; 161 } 162 fromObject(ScriptWrappable * object)163 static ScriptWrappable* fromObject(ScriptWrappable* object) 164 { 165 return object; 166 } 167 setReturnValue(v8::ReturnValue<v8::Value> returnValue)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 markAsDependentGroup(ScriptWrappable * groupRoot,v8::Isolate * isolate)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 setReference(const v8::Persistent<v8::Object> & parent,v8::Isolate * isolate)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> assertWrapperSanity(v8::Local<v8::Object> object,T * objectAsT)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> assertWrapperSanity(void * object,T * objectAsT)204 static void assertWrapperSanity(void* object, T* objectAsT) 205 { 206 ASSERT_NOT_REACHED(); 207 } 208 209 template<typename V8T, typename T> assertWrapperSanity(ScriptWrappable * object,T * objectAsT)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 containsWrapper()221 bool containsWrapper() const { return m_wrapper; } 222 223 #if !ENABLE(OILPAN) 224 protected: ~ScriptWrappable()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: getPersistent(v8::Persistent<v8::Object> * persistent)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 disposeWrapper(v8::Local<v8::Object> wrapper)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 setWeakCallback(const v8::WeakCallbackData<v8::Object,ScriptWrappable> & data)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