• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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