Encapsulate and protect all accesses to the vtable of Heap objects
This is required, so we can safely access the vtable even while we're marking objects during GC. Change-Id: I34f56b61b4bca0d0742faf607eb5ab8b2c30685e Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
This commit is contained in:
parent
f21e8c641a
commit
415f55d140
|
@ -45,7 +45,7 @@ Heap::ArgumentsObject::ArgumentsObject(QV4::CallContext *context)
|
|||
, context(context->d())
|
||||
, fullyCreated(false)
|
||||
{
|
||||
Q_ASSERT(vtable == QV4::ArgumentsObject::staticVTable());
|
||||
Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable());
|
||||
|
||||
ExecutionEngine *v4 = context->d()->engine;
|
||||
Scope scope(v4);
|
||||
|
|
|
@ -106,7 +106,7 @@ struct ArgumentsObject: Object {
|
|||
Heap::MemberData *mappedArguments() { return d()->mappedArguments; }
|
||||
|
||||
static bool isNonStrictArgumentsObject(Managed *m) {
|
||||
return m->d()->vtable->type == Type_ArgumentsObject &&
|
||||
return m->d()->vtable()->type == Type_ArgumentsObject &&
|
||||
!static_cast<ArgumentsObject *>(m)->context()->strictMode;
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ struct ArrayData : public Base {
|
|||
|
||||
bool isSparse() const { return type == Sparse; }
|
||||
|
||||
const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable); }
|
||||
const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); }
|
||||
|
||||
inline ReturnedValue get(uint i) const {
|
||||
return vtable()->get(this, i);
|
||||
|
|
|
@ -81,7 +81,7 @@ struct DateObject: Object {
|
|||
|
||||
template<>
|
||||
inline const DateObject *Value::as() const {
|
||||
return isManaged() && m() && m()->vtable->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : 0;
|
||||
return isManaged() && m() && m()->vtable()->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : 0;
|
||||
}
|
||||
|
||||
struct DateCtor: FunctionObject
|
||||
|
|
|
@ -303,7 +303,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
|
|||
strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
|
||||
|
||||
*static_cast<Value *>(globalObject) = newObject();
|
||||
Q_ASSERT(globalObject->d()->vtable);
|
||||
Q_ASSERT(globalObject->d()->vtable());
|
||||
initRootContext();
|
||||
|
||||
jsObjects[StringProto] = memoryManager->alloc<StringPrototype>(emptyClass, objectPrototype());
|
||||
|
@ -403,7 +403,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
|
|||
//
|
||||
rootContext()->d()->global = globalObject->d();
|
||||
rootContext()->d()->callData->thisObject = globalObject;
|
||||
Q_ASSERT(globalObject->d()->vtable);
|
||||
Q_ASSERT(globalObject->d()->vtable());
|
||||
|
||||
globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor());
|
||||
globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor());
|
||||
|
@ -919,7 +919,7 @@ void ExecutionEngine::markObjects()
|
|||
Q_ASSERT(c->inUse());
|
||||
if (!c->isMarked()) {
|
||||
c->setMarkBit();
|
||||
c->gcGetVtable()->markObjects(c, this);
|
||||
c->vtable()->markObjects(c, this);
|
||||
}
|
||||
c = c->parent;
|
||||
}
|
||||
|
@ -1553,7 +1553,7 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data)
|
|||
|
||||
void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject)
|
||||
{
|
||||
Q_ASSERT(!baseObject.vtable->isObject || static_cast<const Heap::Object&>(baseObject).internalClass->engine == this);
|
||||
Q_ASSERT(!baseObject.vtable()->isObject || static_cast<const Heap::Object&>(baseObject).internalClass->engine == this);
|
||||
Q_UNUSED(baseObject);
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ struct ErrorObject: Object {
|
|||
|
||||
template<>
|
||||
inline const ErrorObject *Value::as() const {
|
||||
return isManaged() && m() && m()->vtable->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : 0;
|
||||
return isManaged() && m() && m()->vtable()->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : 0;
|
||||
}
|
||||
|
||||
struct EvalErrorObject: ErrorObject {
|
||||
|
|
|
@ -208,12 +208,12 @@ Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *sco
|
|||
|
||||
bool FunctionObject::isBinding() const
|
||||
{
|
||||
return d()->vtable == QQmlBindingFunction::staticVTable();
|
||||
return d()->vtable() == QQmlBindingFunction::staticVTable();
|
||||
}
|
||||
|
||||
bool FunctionObject::isBoundFunction() const
|
||||
{
|
||||
return d()->vtable == BoundFunction::staticVTable();
|
||||
return d()->vtable() == BoundFunction::staticVTable();
|
||||
}
|
||||
|
||||
QQmlSourceLocation FunctionObject::sourceLocation() const
|
||||
|
@ -513,7 +513,10 @@ ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *cal
|
|||
ExecutionContextSaver ctxSaver(scope, v4->currentContext());
|
||||
|
||||
CallContext::Data ctx(v4);
|
||||
ctx.vtable = CallContext::staticVTable();
|
||||
#ifndef QT_NO_DEBUG
|
||||
ctx.mm_data = 0; // make sure we don't run into the assertion in setVTable when allocating a context on the stack
|
||||
#endif
|
||||
ctx.setVtable(CallContext::staticVTable());
|
||||
ctx.strictMode = f->strictMode();
|
||||
ctx.callData = callData;
|
||||
ctx.function = f->d();
|
||||
|
@ -548,7 +551,10 @@ ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData
|
|||
ExecutionContextSaver ctxSaver(scope, v4->currentContext());
|
||||
|
||||
CallContext::Data ctx(v4);
|
||||
ctx.vtable = CallContext::staticVTable();
|
||||
#ifndef QT_NO_DEBUG
|
||||
ctx.mm_data = 0; // make sure we don't run into the assertion in setVTable when allocating a context on the stack
|
||||
#endif
|
||||
ctx.setVtable(CallContext::staticVTable());
|
||||
ctx.strictMode = f->strictMode();
|
||||
ctx.callData = callData;
|
||||
ctx.function = f->d();
|
||||
|
@ -604,7 +610,10 @@ ReturnedValue BuiltinFunction::call(const Managed *that, CallData *callData)
|
|||
ExecutionContextSaver ctxSaver(scope, v4->currentContext());
|
||||
|
||||
CallContext::Data ctx(v4);
|
||||
ctx.vtable = CallContext::staticVTable();
|
||||
#ifndef QT_NO_DEBUG
|
||||
ctx.mm_data = 0; // make sure we don't run into the assertion in setVTable when allocating a context on the stack
|
||||
#endif
|
||||
ctx.setVtable(CallContext::staticVTable());
|
||||
ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context?
|
||||
ctx.callData = callData;
|
||||
Q_ASSERT(v4->currentContext() == &ctx);
|
||||
|
@ -625,7 +634,10 @@ ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callDa
|
|||
ExecutionContextSaver ctxSaver(scope, v4->currentContext());
|
||||
|
||||
CallContext::Data ctx(v4);
|
||||
ctx.vtable = CallContext::staticVTable();
|
||||
#ifndef QT_NO_DEBUG
|
||||
ctx.mm_data = 0; // make sure we don't run into the assertion in setVTable when allocating a context on the stack
|
||||
#endif
|
||||
ctx.setVtable(CallContext::staticVTable());
|
||||
ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context?
|
||||
ctx.callData = callData;
|
||||
Q_ASSERT(v4->currentContext() == &ctx);
|
||||
|
|
|
@ -149,7 +149,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
|
|||
|
||||
template<>
|
||||
inline const FunctionObject *Value::as() const {
|
||||
return isManaged() && m() && m()->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : 0;
|
||||
return isManaged() && m() && m()->vtable()->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -80,8 +80,8 @@ public:
|
|||
if (!entry || entry->isMarked())
|
||||
continue;
|
||||
entry->setMarkBit();
|
||||
Q_ASSERT(entry->gcGetVtable()->markObjects);
|
||||
entry->gcGetVtable()->markObjects(entry, e);
|
||||
Q_ASSERT(entry->vtable()->markObjects);
|
||||
entry->vtable()->markObjects(entry, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ const VTable Managed::static_vtbl =
|
|||
QString Managed::className() const
|
||||
{
|
||||
const char *s = 0;
|
||||
switch (Type(d()->vtable->type)) {
|
||||
switch (Type(d()->vtable()->type)) {
|
||||
case Type_Invalid:
|
||||
case Type_String:
|
||||
return QString();
|
||||
|
|
|
@ -148,15 +148,15 @@ public:
|
|||
};
|
||||
Q_MANAGED_TYPE(Invalid)
|
||||
|
||||
bool isListType() const { return d()->vtable->type == Type_QmlSequence; }
|
||||
bool isListType() const { return d()->vtable()->type == Type_QmlSequence; }
|
||||
|
||||
bool isArrayObject() const { return d()->vtable->type == Type_ArrayObject; }
|
||||
bool isStringObject() const { return d()->vtable->type == Type_StringObject; }
|
||||
bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; }
|
||||
bool isStringObject() const { return d()->vtable()->type == Type_StringObject; }
|
||||
|
||||
QString className() const;
|
||||
|
||||
bool isEqualTo(const Managed *other) const
|
||||
{ return d()->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); }
|
||||
{ return d()->vtable()->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); }
|
||||
|
||||
static bool isEqualTo(Managed *m, Managed *other);
|
||||
|
||||
|
@ -180,7 +180,7 @@ inline const Managed *Value::as() const {
|
|||
|
||||
template<>
|
||||
inline const Object *Value::as() const {
|
||||
return isManaged() && m() && m()->vtable->isObject ? objectValue() : 0;
|
||||
return isManaged() && m() && m()->vtable()->isObject ? objectValue() : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -290,7 +290,7 @@ Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attr
|
|||
*attrs = o->arrayData->attributes(index);
|
||||
return p;
|
||||
}
|
||||
if (o->vtable->type == Type_StringObject) {
|
||||
if (o->vtable()->type == Type_StringObject) {
|
||||
if (index < static_cast<const Heap::StringObject *>(o)->length()) {
|
||||
// this is an evil hack, but it works, as the method is only ever called from putIndexed,
|
||||
// where we don't use the returned pointer there for non writable attributes
|
||||
|
|
|
@ -140,7 +140,7 @@ struct Q_QML_EXPORT Object: Managed {
|
|||
const Property *propertyAt(uint index) const { return d()->propertyAt(index); }
|
||||
Property *propertyAt(uint index) { return d()->propertyAt(index); }
|
||||
|
||||
const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable); }
|
||||
const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); }
|
||||
Heap::Object *prototype() const { return d()->prototype; }
|
||||
bool setPrototype(Object *proto);
|
||||
|
||||
|
@ -462,7 +462,7 @@ inline void Object::arraySet(uint index, const Value &value)
|
|||
|
||||
template<>
|
||||
inline const ArrayObject *Value::as() const {
|
||||
return isManaged() && m() && m()->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : 0;
|
||||
return isManaged() && m() && m()->vtable()->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : 0;
|
||||
}
|
||||
|
||||
#ifndef V4_BOOTSTRAP
|
||||
|
|
|
@ -178,8 +178,8 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase)
|
|||
{
|
||||
while (engine->jsStackTop > markBase) {
|
||||
Heap::Base *h = engine->popForGC();
|
||||
Q_ASSERT (h->gcGetVtable()->markObjects);
|
||||
h->gcGetVtable()->markObjects(h, engine);
|
||||
Q_ASSERT (h->vtable()->markObjects);
|
||||
h->vtable()->markObjects(h, engine);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ bool String::isEqualTo(Managed *t, Managed *o)
|
|||
if (t == o)
|
||||
return true;
|
||||
|
||||
if (!o->d()->vtable->isString)
|
||||
if (!o->d()->vtable()->isString)
|
||||
return false;
|
||||
|
||||
return static_cast<String *>(t)->isEqualTo(static_cast<String *>(o));
|
||||
|
|
|
@ -186,7 +186,7 @@ public:
|
|||
|
||||
template<>
|
||||
inline const String *Value::as() const {
|
||||
return isManaged() && m() && m()->vtable->isString ? static_cast<const String *>(this) : 0;
|
||||
return isManaged() && m() && m()->vtable()->isString ? static_cast<const String *>(this) : 0;
|
||||
}
|
||||
|
||||
#ifndef V4_BOOTSTRAP
|
||||
|
|
|
@ -70,7 +70,7 @@ DEFINE_OBJECT_VTABLE(StringObject);
|
|||
Heap::StringObject::StringObject(InternalClass *ic, QV4::Object *prototype)
|
||||
: Heap::Object(ic, prototype)
|
||||
{
|
||||
Q_ASSERT(vtable == QV4::StringObject::staticVTable());
|
||||
Q_ASSERT(vtable() == QV4::StringObject::staticVTable());
|
||||
string = ic->engine->newString();
|
||||
|
||||
Scope scope(ic->engine);
|
||||
|
|
|
@ -329,11 +329,11 @@ struct Q_QML_PRIVATE_EXPORT Value
|
|||
if (!m() || !isManaged())
|
||||
return 0;
|
||||
|
||||
Q_ASSERT(m()->vtable);
|
||||
Q_ASSERT(m()->vtable());
|
||||
#if !defined(QT_NO_QOBJECT_CHECK)
|
||||
static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this));
|
||||
#endif
|
||||
const VTable *vt = m()->vtable;
|
||||
const VTable *vt = m()->vtable();
|
||||
while (vt) {
|
||||
if (vt == T::staticVTable())
|
||||
return static_cast<const T *>(this);
|
||||
|
@ -396,13 +396,13 @@ inline bool Value::isString() const
|
|||
{
|
||||
if (!isManaged())
|
||||
return false;
|
||||
return m() && m()->vtable->isString;
|
||||
return m() && m()->vtable()->isString;
|
||||
}
|
||||
inline bool Value::isObject() const
|
||||
{
|
||||
if (!isManaged())
|
||||
return false;
|
||||
return m() && m()->vtable->isObject;
|
||||
return m() && m()->vtable()->isObject;
|
||||
}
|
||||
|
||||
inline bool Value::isPrimitive() const
|
||||
|
|
|
@ -60,10 +60,7 @@ struct VTable
|
|||
namespace Heap {
|
||||
|
||||
struct Q_QML_EXPORT Base {
|
||||
union {
|
||||
const VTable *vtable;
|
||||
quintptr mm_data;
|
||||
};
|
||||
quintptr mm_data; // vtable and markbit
|
||||
|
||||
inline ReturnedValue asReturnedValue() const;
|
||||
inline void mark(QV4::ExecutionEngine *engine);
|
||||
|
@ -74,7 +71,11 @@ struct Q_QML_EXPORT Base {
|
|||
PointerMask = ~0x3
|
||||
};
|
||||
|
||||
VTable *gcGetVtable() const {
|
||||
void setVtable(const VTable *v) {
|
||||
Q_ASSERT(!(mm_data & MarkBit));
|
||||
mm_data = reinterpret_cast<quintptr>(v);
|
||||
}
|
||||
VTable *vtable() const {
|
||||
return reinterpret_cast<VTable *>(mm_data & PointerMask);
|
||||
}
|
||||
inline bool isMarked() const {
|
||||
|
|
|
@ -183,8 +183,8 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec
|
|||
#ifdef V4_USE_VALGRIND
|
||||
VALGRIND_ENABLE_ERROR_REPORTING;
|
||||
#endif
|
||||
if (m->gcGetVtable()->destroy)
|
||||
m->gcGetVtable()->destroy(m);
|
||||
if (m->vtable()->destroy)
|
||||
m->vtable()->destroy(m);
|
||||
|
||||
memset(m, 0, header->itemSize);
|
||||
#ifdef V4_USE_VALGRIND
|
||||
|
@ -324,8 +324,8 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase)
|
|||
{
|
||||
while (engine->jsStackTop > markBase) {
|
||||
Heap::Base *h = engine->popForGC();
|
||||
Q_ASSERT (h->gcGetVtable()->markObjects);
|
||||
h->gcGetVtable()->markObjects(h, engine);
|
||||
Q_ASSERT (h->vtable()->markObjects);
|
||||
h->vtable()->markObjects(h, engine);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,7 +348,7 @@ void MemoryManager::mark()
|
|||
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
|
||||
if (!(*it).isManaged())
|
||||
continue;
|
||||
if ((*it).managed()->d()->gcGetVtable() != QObjectWrapper::staticVTable())
|
||||
if ((*it).managed()->d()->vtable() != QObjectWrapper::staticVTable())
|
||||
continue;
|
||||
QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*it).managed());
|
||||
if (!qobjectWrapper)
|
||||
|
@ -444,8 +444,8 @@ void MemoryManager::sweep(bool lastSweep)
|
|||
i = i->next;
|
||||
continue;
|
||||
}
|
||||
if (m->gcGetVtable()->destroy)
|
||||
m->gcGetVtable()->destroy(m);
|
||||
if (m->vtable()->destroy)
|
||||
m->vtable()->destroy(m);
|
||||
|
||||
*last = i->next;
|
||||
free(Q_V4_PROFILE_DEALLOC(m_d->engine, i, i->size + sizeof(Data::LargeItem),
|
||||
|
|
|
@ -87,7 +87,7 @@ public:
|
|||
{
|
||||
size = align(size);
|
||||
Heap::Base *o = allocData(size);
|
||||
o->vtable = ManagedType::staticVTable();
|
||||
o->setVtable(ManagedType::staticVTable());
|
||||
return static_cast<typename ManagedType::Data *>(o);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue