blob: 175d7c9d06317256f52a6869048563fdc3625b46 [file] [log] [blame]
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ScriptWrappable_h
#define ScriptWrappable_h
#include "bindings/core/v8/WrapperTypeInfo.h"
#include "platform/ScriptForbiddenScope.h"
#include "platform/heap/Handle.h"
#include <v8.h>
namespace blink {
/**
* The base class of all wrappable objects.
*
* This class provides the internal pointer to be stored in the wrapper objects,
* and its conversions from / to the DOM instances.
*
* Note that this class must not have vtbl (any virtual function) or any member
* variable which increase the size of instances. Some of the classes sensitive
* to the size inherit from this class. So this class must be zero size.
*/
#if COMPILER(MSVC)
// VC++ 2013 doesn't support EBCO (Empty Base Class Optimization). It causes
// that not always pointers to an empty base class are aligned to 4 byte
// alignment. For example,
//
// class EmptyBase1 {};
// class EmptyBase2 {};
// class Derived : public EmptyBase1, public EmptyBase2 {};
// Derived d;
// // &d == 0x1000
// // static_cast<EmptyBase1*>(&d) == 0x1000
// // static_cast<EmptyBase2*>(&d) == 0x1001 // Not 4 byte alignment!
//
// This doesn't happen with other compilers which support EBCO. All the
// addresses in the above example will be 0x1000 with EBCO supported.
//
// Since v8::Object::SetAlignedPointerInInternalField requires the pointers to
// be aligned, we need a hack to specify at least 4 byte alignment to MSVC.
__declspec(align(4))
#endif
class ScriptWrappableBase {
public:
template<typename T>
T* toImpl()
{
// Check if T* is castable to ScriptWrappableBase*, which means T
// doesn't have two or more ScriptWrappableBase as superclasses.
// If T has two ScriptWrappableBase as superclasses, conversions
// from T* to ScriptWrappableBase* are ambiguous.
ASSERT(static_cast<ScriptWrappableBase*>(static_cast<T*>(this)));
// The internal pointers must be aligned to at least 4 byte alignment.
ASSERT((reinterpret_cast<intptr_t>(this) & 0x3) == 0);
return static_cast<T*>(this);
}
ScriptWrappableBase* toScriptWrappableBase()
{
// The internal pointers must be aligned to at least 4 byte alignment.
ASSERT((reinterpret_cast<intptr_t>(this) & 0x3) == 0);
return this;
}
void assertWrapperSanity(v8::Local<v8::Object> object)
{
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object.IsEmpty()
|| object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == toScriptWrappableBase());
}
};
/**
* ScriptWrappable wraps a V8 object and its WrapperTypeInfo.
*
* ScriptWrappable acts much like a v8::Persistent<> in that it keeps a
* V8 object alive.
*
* The state transitions are:
* - new: an empty ScriptWrappable.
* - setWrapper: install a v8::Persistent (or empty)
* - disposeWrapper (via setWeakCallback, triggered by V8 garbage collecter):
* remove v8::Persistent and become empty.
*/
class ScriptWrappable : public ScriptWrappableBase {
public:
ScriptWrappable() { }
// Returns the WrapperTypeInfo of the instance.
//
// This method must be overridden by DEFINE_WRAPPERTYPEINFO macro.
virtual const WrapperTypeInfo* wrapperTypeInfo() const = 0;
// Creates and returns a new wrapper object.
virtual v8::Handle<v8::Object> wrap(v8::Handle<v8::Object> creationContext, v8::Isolate*);
// Associates the instance with the existing wrapper. Returns |wrapper|.
virtual v8::Handle<v8::Object> associateWithWrapper(const WrapperTypeInfo*, v8::Handle<v8::Object> wrapper, v8::Isolate*);
void setWrapper(v8::Handle<v8::Object> wrapper, v8::Isolate* isolate, const WrapperTypeInfo* wrapperTypeInfo)
{
ASSERT(!containsWrapper());
if (!*wrapper)
return;
m_wrapper.Reset(isolate, wrapper);
wrapperTypeInfo->configureWrapper(&m_wrapper);
m_wrapper.SetWeak(this, &setWeakCallback);
ASSERT(containsWrapper());
}
v8::Local<v8::Object> newLocalWrapper(v8::Isolate* isolate) const
{
return v8::Local<v8::Object>::New(isolate, m_wrapper);
}
bool isEqualTo(const v8::Local<v8::Object>& other) const
{
return m_wrapper == other;
}
static bool wrapperCanBeStoredInObject(const void*) { return false; }
static bool wrapperCanBeStoredInObject(const ScriptWrappable*) { return true; }
static ScriptWrappable* fromObject(const void*)
{
ASSERT_NOT_REACHED();
return 0;
}
static ScriptWrappable* fromObject(ScriptWrappable* object)
{
return object;
}
// Provides a way to convert Node* to ScriptWrappable* without including
// "core/dom/Node.h".
//
// Example:
// void foo(const void*) { ... } // [1]
// void foo(ScriptWrappable*) { ... } // [2]
// class Node;
// Node* node;
// foo(node); // This calls [1] because there is no definition of Node
// // and compilers do not know that Node is a subclass of
// // ScriptWrappable.
// foo(ScriptWrappable::fromNode(node)); // This calls [2] as expected.
//
// The definition of fromNode is placed in Node.h because we'd like to
// inline calls to fromNode as much as possible.
static ScriptWrappable* fromNode(Node*);
bool setReturnValue(v8::ReturnValue<v8::Value> returnValue)
{
returnValue.Set(m_wrapper);
return containsWrapper();
}
void markAsDependentGroup(ScriptWrappable* groupRoot, v8::Isolate* isolate)
{
ASSERT(containsWrapper());
ASSERT(groupRoot && groupRoot->containsWrapper());
// FIXME: There has to be a better way.
v8::UniqueId groupId(*reinterpret_cast<intptr_t*>(&groupRoot->m_wrapper));
m_wrapper.MarkPartiallyDependent();
isolate->SetObjectGroupId(v8::Persistent<v8::Value>::Cast(m_wrapper), groupId);
}
void setReference(const v8::Persistent<v8::Object>& parent, v8::Isolate* isolate)
{
isolate->SetReference(parent, m_wrapper);
}
template<typename V8T, typename T>
static void assertWrapperSanity(v8::Local<v8::Object> object, T* objectAsT)
{
ASSERT(objectAsT);
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object.IsEmpty()
|| object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == V8T::toScriptWrappableBase(objectAsT));
}
template<typename V8T, typename T>
static void assertWrapperSanity(void* object, T* objectAsT)
{
ASSERT_NOT_REACHED();
}
template<typename V8T, typename T>
static void assertWrapperSanity(ScriptWrappable* object, T* objectAsT)
{
ASSERT(object);
ASSERT(objectAsT);
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object->m_wrapper.IsEmpty()
|| v8::Object::GetAlignedPointerFromInternalField(object->m_wrapper, v8DOMWrapperObjectIndex) == V8T::toScriptWrappableBase(objectAsT));
}
using ScriptWrappableBase::assertWrapperSanity;
bool containsWrapper() const { return !m_wrapper.IsEmpty(); }
#if !ENABLE(OILPAN)
protected:
virtual ~ScriptWrappable()
{
// We must not get deleted as long as we contain a wrapper. If this happens, we screwed up ref
// counting somewhere. Crash here instead of crashing during a later gc cycle.
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper());
}
#endif
// With Oilpan we don't need a ScriptWrappable destructor.
//
// - 'RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper())' is not needed
// because Oilpan is not using reference counting at all. If containsWrapper() is true,
// it means that ScriptWrappable still has a wrapper. In this case, the destructor
// must not be called since the wrapper has a persistent handle back to this ScriptWrappable object.
// Assuming that Oilpan's GC is correct (If we cannot assume this, a lot of more things are
// already broken), we must not hit the RELEASE_ASSERT.
private:
void disposeWrapper(v8::Local<v8::Object> wrapper)
{
ASSERT(containsWrapper());
ASSERT(wrapper == m_wrapper);
m_wrapper.Reset();
}
static void setWeakCallback(const v8::WeakCallbackData<v8::Object, ScriptWrappable>& data)
{
data.GetParameter()->disposeWrapper(data.GetValue());
// FIXME: I noticed that 50%~ of minor GC cycle times can be consumed
// inside data.GetParameter()->deref(), which causes Node destructions. We should
// make Node destructions incremental.
releaseObject(data.GetValue());
}
v8::Persistent<v8::Object> m_wrapper;
};
// Defines 'wrapperTypeInfo' virtual method which returns the WrapperTypeInfo of
// the instance. Also declares a static member of type WrapperTypeInfo, of which
// the definition is given by the IDL code generator.
//
// Every DOM Class T must meet either of the following conditions:
// - T.idl inherits from [NotScriptWrappable].
// - T inherits from ScriptWrappable and has DEFINE_WRAPPERTYPEINFO().
//
// If a DOM class T does not inherit from ScriptWrappable, you have to write
// [NotScriptWrappable] in the IDL file as an extended attribute in order to let
// IDL code generator know that T does not inherit from ScriptWrappable. Note
// that [NotScriptWrappable] is inheritable.
//
// All the derived classes of ScriptWrappable, regardless of directly or
// indirectly, must write this macro in the class definition.
#define DEFINE_WRAPPERTYPEINFO() \
public: \
virtual const WrapperTypeInfo* wrapperTypeInfo() const override \
{ \
return &s_wrapperTypeInfo; \
} \
private: \
static const WrapperTypeInfo& s_wrapperTypeInfo
} // namespace blink
#endif // ScriptWrappable_h