#ifndef ScriptWrappable_h
#define ScriptWrappable_h
-#include "bindings/v8/UnsafePersistent.h"
#include "bindings/v8/WrapperTypeInfo.h"
-#include "heap/Handle.h"
+#include "platform/heap/Handle.h"
#include <v8.h>
// Helper to call webCoreInitializeScriptWrappableForInterface in the global namespace.
namespace WebCore {
+/**
+ * ScriptWrappable wraps a V8 object and its WrapperTypeInfo.
+ *
+ * ScriptWrappable acts much like a v8::Persistent<> in that it keeps a
+ * V8 object alive. Under the hood, however, it keeps either a TypeInfo
+ * object or an actual v8 persistent (or is empty).
+ *
+ * The physical state space of ScriptWrappable is:
+ * - uintptr_t m_wrapperOrTypeInfo;
+ * - if 0: the ScriptWrappable is uninitialized/empty.
+ * - if even: a pointer to WebCore::TypeInfo
+ * - if odd: a pointer to v8::Persistent<v8::Object> + 1.
+ *
+ * In other words, one integer represents one of two object pointers,
+ * depending on its least signficiant bit, plus an uninitialized state.
+ * This class is meant to mask the logistics behind this.
+ *
+ * typeInfo() and newLocalWrapper will return appropriate values (possibly
+ * 0/empty) in all physical states.
+ *
+ * The state transitions are:
+ * - new: an empty and invalid ScriptWrappable.
+ * - init (to be called by all subclasses in their constructor):
+ * needs to call setTypeInfo
+ * - setTypeInfo: install a WrapperTypeInfo
+ * - setWrapper: install a v8::Persistent (or empty)
+ * - disposeWrapper (via setWeakCallback, triggered by V8 garbage collecter):
+ * remove v8::Persistent and install a TypeInfo of the previous value.
+ */
class ScriptWrappable {
public:
ScriptWrappable() : m_wrapperOrTypeInfo(0) { }
v8::Local<v8::Object> newLocalWrapper(v8::Isolate* isolate) const
{
- return unsafePersistent().newLocal(isolate);
+ v8::Persistent<v8::Object> persistent;
+ getPersistent(&persistent);
+ return v8::Local<v8::Object>::New(isolate, persistent);
}
const WrapperTypeInfo* typeInfo()
if (containsTypeInfo())
return reinterpret_cast<const WrapperTypeInfo*>(m_wrapperOrTypeInfo);
- if (containsWrapper())
- return toWrapperTypeInfo(*(unsafePersistent().persistent()));
+ if (containsWrapper()) {
+ v8::Persistent<v8::Object> persistent;
+ getPersistent(&persistent);
+ return toWrapperTypeInfo(persistent);
+ }
return 0;
}
ASSERT(containsTypeInfo());
}
+ bool isEqualTo(const v8::Local<v8::Object>& other) const
+ {
+ v8::Persistent<v8::Object> persistent;
+ getPersistent(&persistent);
+ return persistent == other;
+ }
+
static bool wrapperCanBeStoredInObject(const void*) { return false; }
static bool wrapperCanBeStoredInObject(const ScriptWrappable*) { return true; }
- static void setWrapperInObject(void*, v8::Handle<v8::Object>, v8::Isolate*, const WrapperConfiguration&)
+ static ScriptWrappable* fromObject(const void*)
{
ASSERT_NOT_REACHED();
+ return 0;
}
- static void setWrapperInObject(ScriptWrappable* object, v8::Handle<v8::Object> wrapper, v8::Isolate* isolate, const WrapperConfiguration& configuration)
+ static ScriptWrappable* fromObject(ScriptWrappable* object)
{
- object->setWrapper(wrapper, isolate, configuration);
+ return object;
}
- static const WrapperTypeInfo* getTypeInfoFromObject(void* object)
+ bool setReturnValue(v8::ReturnValue<v8::Value> returnValue)
{
- ASSERT_NOT_REACHED();
- return 0;
+ v8::Persistent<v8::Object> persistent;
+ getPersistent(&persistent);
+ returnValue.Set(persistent);
+ return containsWrapper();
}
- static const WrapperTypeInfo* getTypeInfoFromObject(ScriptWrappable* object)
+ void markAsDependentGroup(ScriptWrappable* groupRoot, v8::Isolate* isolate)
{
- return object->typeInfo();
+ ASSERT(containsWrapper());
+ ASSERT(groupRoot && groupRoot->containsWrapper());
+
+ v8::UniqueId groupId(groupRoot->m_wrapperOrTypeInfo);
+ v8::Persistent<v8::Object> wrapper;
+ getPersistent(&wrapper);
+ wrapper.MarkPartiallyDependent();
+ isolate->SetObjectGroupId(v8::Persistent<v8::Value>::Cast(wrapper), groupId);
}
- static void setTypeInfoInObject(void* object, const WrapperTypeInfo*)
+ void setReference(const v8::Persistent<v8::Object>& parent, v8::Isolate* isolate)
{
- ASSERT_NOT_REACHED();
+ v8::Persistent<v8::Object> persistent;
+ getPersistent(&persistent);
+ isolate->SetReference(parent, persistent);
}
- static void setTypeInfoInObject(ScriptWrappable* object, const WrapperTypeInfo* typeInfo)
+ template<typename V8T, typename T>
+ static void assertWrapperSanity(v8::Local<v8::Object> object, T* objectAsT)
{
- object->setTypeInfo(typeInfo);
+ ASSERT(objectAsT);
+ RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object.IsEmpty()
+ || object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == V8T::toInternalPointer(objectAsT));
}
template<typename V8T, typename T>
- static bool setReturnValueWithSecurityCheck(v8::ReturnValue<v8::Value> returnValue, T* object)
+ static void assertWrapperSanity(void* object, T* objectAsT)
{
- return ScriptWrappable::getUnsafeWrapperFromObject(object).template setReturnValueWithSecurityCheck<V8T>(returnValue, object);
+ ASSERT_NOT_REACHED();
}
- template<typename T>
- static bool setReturnValue(v8::ReturnValue<v8::Value> returnValue, T* object)
+ template<typename V8T, typename T>
+ static void assertWrapperSanity(ScriptWrappable* object, T* objectAsT)
{
- return ScriptWrappable::getUnsafeWrapperFromObject(object).setReturnValue(returnValue);
+ ASSERT(object);
+ ASSERT(objectAsT);
+ v8::Object* value = object->getRawValue();
+ RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(value == 0
+ || value->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex) == V8T::toInternalPointer(objectAsT));
}
+ inline bool containsWrapper() const { return (m_wrapperOrTypeInfo & 1); }
+ inline bool containsTypeInfo() const { return m_wrapperOrTypeInfo && !(m_wrapperOrTypeInfo & 1); }
+
protected:
~ScriptWrappable()
{
- ASSERT(m_wrapperOrTypeInfo); // Assert initialization via init() even if not subsequently wrapped.
- m_wrapperOrTypeInfo = 0; // Break UAF attempts to wrap.
+ // 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());
+ ASSERT(m_wrapperOrTypeInfo); // Assert initialization via init() even if not subsequently wrapped.
+ m_wrapperOrTypeInfo = 0; // Break UAF attempts to wrap.
}
private:
- // For calling unsafePersistent and getWrapperFromObject.
- friend class MinorGCWrapperVisitor;
- friend class DOMDataStore;
-
- UnsafePersistent<v8::Object> unsafePersistent() const
+ void getPersistent(v8::Persistent<v8::Object>* persistent) const
{
- v8::Object* object = containsWrapper() ? reinterpret_cast<v8::Object*>(m_wrapperOrTypeInfo & ~1) : 0;
- return UnsafePersistent<v8::Object>(object);
- }
+ ASSERT(persistent);
- static UnsafePersistent<v8::Object> getUnsafeWrapperFromObject(void*)
- {
- ASSERT_NOT_REACHED();
- return UnsafePersistent<v8::Object>();
+ // Horrible and super unsafe: Cast the Persistent to an Object*, so
+ // that we can inject the wrapped value. This only works because
+ // we previously 'stole' the object pointer from a Persistent in
+ // the setWrapper() method.
+ *reinterpret_cast<v8::Object**>(persistent) = getRawValue();
}
- static UnsafePersistent<v8::Object> getUnsafeWrapperFromObject(ScriptWrappable* object)
+ inline v8::Object* getRawValue() const
{
- return object->unsafePersistent();
+ v8::Object* object = containsWrapper() ? reinterpret_cast<v8::Object*>(m_wrapperOrTypeInfo & ~1) : 0;
+ return object;
}
- inline bool containsWrapper() const { return (m_wrapperOrTypeInfo & 1) == 1; }
- inline bool containsTypeInfo() const { return m_wrapperOrTypeInfo && (m_wrapperOrTypeInfo & 1) == 0; }
-
inline void disposeWrapper(v8::Local<v8::Object> wrapper)
{
ASSERT(containsWrapper());
- ASSERT(wrapper == *unsafePersistent().persistent());
- unsafePersistent().dispose();
+
+ v8::Persistent<v8::Object> persistent;
+ getPersistent(&persistent);
+
+ ASSERT(wrapper == persistent);
+ persistent.Reset();
setTypeInfo(toWrapperTypeInfo(wrapper));
}
static void setWeakCallback(const v8::WeakCallbackData<v8::Object, ScriptWrappable>& data)
{
- ASSERT(*data.GetParameter()->unsafePersistent().persistent() == data.GetValue());
+ v8::Persistent<v8::Object> persistent;
+ data.GetParameter()->getPersistent(&persistent);
+ ASSERT(persistent == data.GetValue());
data.GetParameter()->disposeWrapper(data.GetValue());
// FIXME: I noticed that 50%~ of minor GC cycle times can be consumed