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/v8/WrapperTypeInfo.h" 35 #include "platform/heap/Handle.h" 36 #include <v8.h> 37 38 // Helper to call webCoreInitializeScriptWrappableForInterface in the global namespace. 39 template <class C> inline void initializeScriptWrappableHelper(C* object) 40 { 41 void webCoreInitializeScriptWrappableForInterface(C*); 42 webCoreInitializeScriptWrappableForInterface(object); 43 } 44 45 namespace WebCore { 46 47 /** 48 * ScriptWrappable wraps a V8 object and its WrapperTypeInfo. 49 * 50 * ScriptWrappable acts much like a v8::Persistent<> in that it keeps a 51 * V8 object alive. Under the hood, however, it keeps either a TypeInfo 52 * object or an actual v8 persistent (or is empty). 53 * 54 * The physical state space of ScriptWrappable is: 55 * - uintptr_t m_wrapperOrTypeInfo; 56 * - if 0: the ScriptWrappable is uninitialized/empty. 57 * - if even: a pointer to WebCore::TypeInfo 58 * - if odd: a pointer to v8::Persistent<v8::Object> + 1. 59 * 60 * In other words, one integer represents one of two object pointers, 61 * depending on its least signficiant bit, plus an uninitialized state. 62 * This class is meant to mask the logistics behind this. 63 * 64 * typeInfo() and newLocalWrapper will return appropriate values (possibly 65 * 0/empty) in all physical states. 66 * 67 * The state transitions are: 68 * - new: an empty and invalid ScriptWrappable. 69 * - init (to be called by all subclasses in their constructor): 70 * needs to call setTypeInfo 71 * - setTypeInfo: install a WrapperTypeInfo 72 * - setWrapper: install a v8::Persistent (or empty) 73 * - disposeWrapper (via setWeakCallback, triggered by V8 garbage collecter): 74 * remove v8::Persistent and install a TypeInfo of the previous value. 75 */ 76 class ScriptWrappable { 77 public: 78 ScriptWrappable() : m_wrapperOrTypeInfo(0) { } 79 80 // Wrappables need to be initialized with their most derrived type for which 81 // bindings exist, in much the same way that certain other types need to be 82 // adopted and so forth. The overloaded initializeScriptWrappableForInterface() 83 // functions are implemented by the generated V8 bindings code. Declaring the 84 // extern function in the template avoids making a centralized header of all 85 // the bindings in the universe. C++11's extern template feature may provide 86 // a cleaner solution someday. 87 template <class C> static void init(C* object) 88 { 89 initializeScriptWrappableHelper(object); 90 } 91 92 void setWrapper(v8::Handle<v8::Object> wrapper, v8::Isolate* isolate, const WrapperConfiguration& configuration) 93 { 94 ASSERT(!containsWrapper()); 95 if (!*wrapper) { 96 m_wrapperOrTypeInfo = 0; 97 return; 98 } 99 v8::Persistent<v8::Object> persistent(isolate, wrapper); 100 configuration.configureWrapper(&persistent); 101 persistent.SetWeak(this, &setWeakCallback); 102 m_wrapperOrTypeInfo = reinterpret_cast<uintptr_t>(persistent.ClearAndLeak()) | 1; 103 ASSERT(containsWrapper()); 104 } 105 106 v8::Local<v8::Object> newLocalWrapper(v8::Isolate* isolate) const 107 { 108 v8::Persistent<v8::Object> persistent; 109 getPersistent(&persistent); 110 return v8::Local<v8::Object>::New(isolate, persistent); 111 } 112 113 const WrapperTypeInfo* typeInfo() 114 { 115 if (containsTypeInfo()) 116 return reinterpret_cast<const WrapperTypeInfo*>(m_wrapperOrTypeInfo); 117 118 if (containsWrapper()) { 119 v8::Persistent<v8::Object> persistent; 120 getPersistent(&persistent); 121 return toWrapperTypeInfo(persistent); 122 } 123 124 return 0; 125 } 126 127 void setTypeInfo(const WrapperTypeInfo* typeInfo) 128 { 129 m_wrapperOrTypeInfo = reinterpret_cast<uintptr_t>(typeInfo); 130 ASSERT(containsTypeInfo()); 131 } 132 133 bool isEqualTo(const v8::Local<v8::Object>& other) const 134 { 135 v8::Persistent<v8::Object> persistent; 136 getPersistent(&persistent); 137 return persistent == other; 138 } 139 140 static bool wrapperCanBeStoredInObject(const void*) { return false; } 141 static bool wrapperCanBeStoredInObject(const ScriptWrappable*) { return true; } 142 143 static ScriptWrappable* fromObject(const void*) 144 { 145 ASSERT_NOT_REACHED(); 146 return 0; 147 } 148 149 static ScriptWrappable* fromObject(ScriptWrappable* object) 150 { 151 return object; 152 } 153 154 bool setReturnValue(v8::ReturnValue<v8::Value> returnValue) 155 { 156 v8::Persistent<v8::Object> persistent; 157 getPersistent(&persistent); 158 returnValue.Set(persistent); 159 return containsWrapper(); 160 } 161 162 void markAsDependentGroup(ScriptWrappable* groupRoot, v8::Isolate* isolate) 163 { 164 ASSERT(containsWrapper()); 165 ASSERT(groupRoot && groupRoot->containsWrapper()); 166 167 v8::UniqueId groupId(groupRoot->m_wrapperOrTypeInfo); 168 v8::Persistent<v8::Object> wrapper; 169 getPersistent(&wrapper); 170 wrapper.MarkPartiallyDependent(); 171 isolate->SetObjectGroupId(v8::Persistent<v8::Value>::Cast(wrapper), groupId); 172 } 173 174 void setReference(const v8::Persistent<v8::Object>& parent, v8::Isolate* isolate) 175 { 176 v8::Persistent<v8::Object> persistent; 177 getPersistent(&persistent); 178 isolate->SetReference(parent, persistent); 179 } 180 181 template<typename V8T, typename T> 182 static void assertWrapperSanity(v8::Local<v8::Object> object, T* objectAsT) 183 { 184 ASSERT(objectAsT); 185 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object.IsEmpty() 186 || object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == V8T::toInternalPointer(objectAsT)); 187 } 188 189 template<typename V8T, typename T> 190 static void assertWrapperSanity(void* object, T* objectAsT) 191 { 192 ASSERT_NOT_REACHED(); 193 } 194 195 template<typename V8T, typename T> 196 static void assertWrapperSanity(ScriptWrappable* object, T* objectAsT) 197 { 198 ASSERT(object); 199 ASSERT(objectAsT); 200 v8::Object* value = object->getRawValue(); 201 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(value == 0 202 || value->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == V8T::toInternalPointer(objectAsT)); 203 } 204 205 inline bool containsWrapper() const { return (m_wrapperOrTypeInfo & 1); } 206 inline bool containsTypeInfo() const { return m_wrapperOrTypeInfo && !(m_wrapperOrTypeInfo & 1); } 207 208 protected: 209 ~ScriptWrappable() 210 { 211 // We must not get deleted as long as we contain a wrapper. If this happens, we screwed up ref 212 // counting somewhere. Crash here instead of crashing during a later gc cycle. 213 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper()); 214 ASSERT(m_wrapperOrTypeInfo); // Assert initialization via init() even if not subsequently wrapped. 215 m_wrapperOrTypeInfo = 0; // Break UAF attempts to wrap. 216 } 217 218 private: 219 void getPersistent(v8::Persistent<v8::Object>* persistent) const 220 { 221 ASSERT(persistent); 222 223 // Horrible and super unsafe: Cast the Persistent to an Object*, so 224 // that we can inject the wrapped value. This only works because 225 // we previously 'stole' the object pointer from a Persistent in 226 // the setWrapper() method. 227 *reinterpret_cast<v8::Object**>(persistent) = getRawValue(); 228 } 229 230 inline v8::Object* getRawValue() const 231 { 232 v8::Object* object = containsWrapper() ? reinterpret_cast<v8::Object*>(m_wrapperOrTypeInfo & ~1) : 0; 233 return object; 234 } 235 236 inline void disposeWrapper(v8::Local<v8::Object> wrapper) 237 { 238 ASSERT(containsWrapper()); 239 240 v8::Persistent<v8::Object> persistent; 241 getPersistent(&persistent); 242 243 ASSERT(wrapper == persistent); 244 persistent.Reset(); 245 setTypeInfo(toWrapperTypeInfo(wrapper)); 246 } 247 248 // If zero, then this contains nothing, otherwise: 249 // If the bottom bit it set, then this contains a pointer to a wrapper object in the remainging bits. 250 // If the bottom bit is clear, then this contains a pointer to the wrapper type info in the remaining bits. 251 uintptr_t m_wrapperOrTypeInfo; 252 253 static void setWeakCallback(const v8::WeakCallbackData<v8::Object, ScriptWrappable>& data) 254 { 255 v8::Persistent<v8::Object> persistent; 256 data.GetParameter()->getPersistent(&persistent); 257 ASSERT(persistent == data.GetValue()); 258 data.GetParameter()->disposeWrapper(data.GetValue()); 259 260 // FIXME: I noticed that 50%~ of minor GC cycle times can be consumed 261 // inside data.GetParameter()->deref(), which causes Node destructions. We should 262 // make Node destructions incremental. 263 releaseObject(data.GetValue()); 264 } 265 }; 266 267 } // namespace WebCore 268 269 #endif // ScriptWrappable_h 270