Garbage collect member data
authorLars Knoll <lars.knoll@digia.com>
Mon, 31 Mar 2014 13:48:02 +0000 (15:48 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 4 Apr 2014 15:26:20 +0000 (17:26 +0200)
Move the allocated member data into the garbage collected
area, so that we can avoid using malloc/free for it.

Change-Id: I20625efa67ecd60238568742b74854b0c8cb2e3e
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
12 files changed:
src/qml/jsruntime/jsruntime.pri
src/qml/jsruntime/qv4argumentsobject.cpp
src/qml/jsruntime/qv4argumentsobject_p.h
src/qml/jsruntime/qv4engine.cpp
src/qml/jsruntime/qv4engine_p.h
src/qml/jsruntime/qv4functionobject.cpp
src/qml/jsruntime/qv4internalclass.cpp
src/qml/jsruntime/qv4lookup.cpp
src/qml/jsruntime/qv4memberdata.cpp [new file with mode: 0644]
src/qml/jsruntime/qv4memberdata_p.h [new file with mode: 0644]
src/qml/jsruntime/qv4object.cpp
src/qml/jsruntime/qv4object_p.h

index ac26794..9d5757b 100644 (file)
@@ -26,6 +26,7 @@ SOURCES += \
     $$PWD/qv4globalobject.cpp \
     $$PWD/qv4jsonobject.cpp \
     $$PWD/qv4mathobject.cpp \
+    $$PWD/qv4memberdata.cpp \
     $$PWD/qv4numberobject.cpp \
     $$PWD/qv4object.cpp \
     $$PWD/qv4objectproto.cpp \
@@ -73,6 +74,7 @@ HEADERS += \
     $$PWD/qv4globalobject_p.h \
     $$PWD/qv4jsonobject_p.h \
     $$PWD/qv4mathobject_p.h \
+    $$PWD/qv4memberdata_p.h \
     $$PWD/qv4numberobject_p.h \
     $$PWD/qv4object_p.h \
     $$PWD/qv4objectproto_p.h \
index c8ba2c4..1c210b5 100644 (file)
@@ -93,8 +93,9 @@ void ArgumentsObject::fullyCreate()
     uint argCount = qMin(context->realArgumentCount, context->callData->argc);
     ArrayData::realloc(this, ArrayData::Sparse, 0, argCount, true);
     context->engine->requireArgumentsAccessors(numAccessors);
+    mappedArguments.ensureIndex(engine(), numAccessors);
     for (uint i = 0; i < (uint)numAccessors; ++i) {
-        mappedArguments.append(context->callData->args[i]);
+        mappedArguments[i] = context->callData->args[i];
         arraySet(i, context->engine->argumentsAccessors[i], Attr_Accessor);
     }
     arrayPut(numAccessors, context->callData->args + numAccessors, argCount - numAccessors);
@@ -113,7 +114,8 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
     Property map;
     PropertyAttributes mapAttrs;
     bool isMapped = false;
-    if (pd && index < (uint)mappedArguments.size())
+    uint numAccessors = qMin((int)context->function->formalParameterCount(), context->realArgumentCount);
+    if (pd && index < (uint)numAccessors)
         isMapped = arrayData->attributes(index).isAccessor() && pd->getter() == context->engine->argumentsAccessors[index].getter();
 
     if (isMapped) {
@@ -121,7 +123,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
         map.copy(*pd, mapAttrs);
         setArrayAttributes(index, Attr_Data);
         pd = arrayData->getProperty(index);
-        pd->value = mappedArguments.at(index);
+        pd->value = mappedArguments[index];
     }
 
     bool strict = ctx->strictMode;
@@ -232,9 +234,9 @@ ReturnedValue ArgumentsSetterFunction::call(Managed *setter, CallData *callData)
 void ArgumentsObject::markObjects(Managed *that, ExecutionEngine *e)
 {
     ArgumentsObject *o = static_cast<ArgumentsObject *>(that);
-    o->context->mark(e);
-    for (int i = 0; i < o->mappedArguments.size(); ++i)
-        o->mappedArguments.at(i).mark(e);
+    if (o->context)
+        o->context->mark(e);
+    o->mappedArguments.mark(e);
 
     Object::markObjects(that, e);
 }
index a790410..80c2a70 100644 (file)
@@ -80,7 +80,7 @@ struct ArgumentsObject: Object {
     Q_MANAGED_TYPE(ArgumentsObject)
     CallContext *context;
     bool fullyCreated;
-    QVector<Value> mappedArguments;
+    Members mappedArguments;
     ArgumentsObject(CallContext *context);
     ~ArgumentsObject() {}
 
index 5372ef8..b95197e 100644 (file)
@@ -68,6 +68,7 @@
 #include "qv4sequenceobject_p.h"
 #include "qv4qobjectwrapper_p.h"
 #include "qv4qmlextensions_p.h"
+#include "qv4memberdata_p.h"
 
 #include <QtCore/QTextStream>
 
@@ -253,6 +254,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
     id_destroy = newIdentifier(QStringLiteral("destroy"));
     id_valueOf = newIdentifier(QStringLiteral("valueOf"));
 
+    memberDataClass = InternalClass::create(this, MemberData::staticVTable(), 0);
+
     ObjectPrototype *objectPrototype = new (memoryManager) ObjectPrototype(InternalClass::create(this, ObjectPrototype::staticVTable(), 0));
     objectClass = InternalClass::create(this, Object::staticVTable(), objectPrototype);
     Q_ASSERT(objectClass->vtable == Object::staticVTable());
@@ -286,9 +289,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
     DatePrototype *datePrototype = new (memoryManager) DatePrototype(InternalClass::create(this, DatePrototype::staticVTable(), objectPrototype));
     dateClass = InternalClass::create(this, DateObject::staticVTable(), datePrototype);
 
-    FunctionPrototype *functionPrototype = new (memoryManager) FunctionPrototype(InternalClass::create(this, FunctionPrototype::staticVTable(), objectPrototype));
-    functionClass = InternalClass::create(this, FunctionObject::staticVTable(), functionPrototype);
+    InternalClass *functionProtoClass = InternalClass::create(this, FunctionObject::staticVTable(), objectPrototype);
     uint index;
+    functionProtoClass = functionProtoClass->addMember(id_prototype, Attr_NotEnumerable, &index);
+    Q_ASSERT(index == FunctionObject::Index_Prototype);
+    FunctionPrototype *functionPrototype = new (memoryManager) FunctionPrototype(functionProtoClass);
+    functionClass = InternalClass::create(this, FunctionObject::staticVTable(), functionPrototype);
     functionClass = functionClass->addMember(id_prototype, Attr_NotEnumerable|Attr_NotConfigurable, &index);
     Q_ASSERT(index == FunctionObject::Index_Prototype);
     protoClass = objectClass->addMember(id_constructor, Attr_NotEnumerable, &index);
index 8620913..b93af51 100644 (file)
@@ -229,6 +229,7 @@ public:
     InternalClass *strictArgumentsObjectClass;
 
     InternalClass *variantClass;
+    InternalClass *memberDataClass;
 
     EvalFunction *evalFunction;
     FunctionObject *thrower;
index 00c13a1..8e943fa 100644 (file)
@@ -167,7 +167,8 @@ ReturnedValue FunctionObject::call(Managed *, CallData *)
 void FunctionObject::markObjects(Managed *that, ExecutionEngine *e)
 {
     FunctionObject *o = static_cast<FunctionObject *>(that);
-    o->scope->mark(e);
+    if (o->scope)
+        o->scope->mark(e);
 
     Object::markObjects(that, e);
 }
index 4fe8f0b..61608b4 100644 (file)
@@ -160,10 +160,10 @@ void InternalClass::changeMember(Object *object, String *string, PropertyAttribu
 
     if (newClass->size > object->internalClass->size) {
         Q_ASSERT(newClass->size == object->internalClass->size + 1);
-        memmove(object->memberData + idx + 2, object->memberData + idx + 1, (object->internalClass->size - idx - 1)*sizeof(Value));
+        memmove(object->memberData.data() + idx + 2, object->memberData.data() + idx + 1, (object->internalClass->size - idx - 1)*sizeof(Value));
     } else if (newClass->size < object->internalClass->size) {
         Q_ASSERT(newClass->size == object->internalClass->size - 1);
-        memmove(object->memberData + idx + 1, object->memberData + idx + 2, (object->internalClass->size - idx - 2)*sizeof(Value));
+        memmove(object->memberData.data() + idx + 1, object->memberData.data() + idx + 2, (object->internalClass->size - idx - 2)*sizeof(Value));
     }
     object->internalClass = newClass;
 }
@@ -368,7 +368,7 @@ void InternalClass::removeMember(Object *object, Identifier *id)
     }
 
     // remove the entry in memberdata
-    memmove(object->memberData + propIdx, object->memberData + propIdx + 1, (object->internalClass->size - propIdx)*sizeof(Value));
+    memmove(object->memberData.data() + propIdx, object->memberData.data() + propIdx + 1, (object->internalClass->size - propIdx)*sizeof(Value));
 
     oldClass->transitions.insert(t, object->internalClass);
 }
index 875a027..1155bbf 100644 (file)
@@ -763,7 +763,7 @@ void Lookup::setterInsert0(Lookup *l, const ValueRef object, const ValueRef valu
     Object *o = static_cast<Object *>(object->asManaged());
     if (o && o->internalClass == l->classList[0]) {
         if (!o->prototype()) {
-            if (l->index >= o->memberDataAlloc)
+            if (l->index >= o->memberData.size())
                 o->ensureMemberIndex(l->index);
             o->memberData[l->index] = *value;
             o->internalClass = l->classList[3];
@@ -781,7 +781,7 @@ void Lookup::setterInsert1(Lookup *l, const ValueRef object, const ValueRef valu
     if (o && o->internalClass == l->classList[0]) {
         Object *p = o->prototype();
         if (p && p->internalClass == l->classList[1]) {
-            if (l->index >= o->memberDataAlloc)
+            if (l->index >= o->memberData.size())
                 o->ensureMemberIndex(l->index);
             o->memberData[l->index] = *value;
             o->internalClass = l->classList[3];
@@ -801,7 +801,7 @@ void Lookup::setterInsert2(Lookup *l, const ValueRef object, const ValueRef valu
         if (p && p->internalClass == l->classList[1]) {
             p = p->prototype();
             if (p && p->internalClass == l->classList[2]) {
-                if (l->index >= o->memberDataAlloc)
+                if (l->index >= o->memberData.size())
                     o->ensureMemberIndex(l->index);
                 o->memberData[l->index] = *value;
                 o->internalClass = l->classList[3];
diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp
new file mode 100644 (file)
index 0000000..aeb4c38
--- /dev/null
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4memberdata_p.h"
+#include "qv4mm_p.h"
+
+using namespace QV4;
+
+const ManagedVTable MemberData::static_vtbl =
+{
+    MemberData::IsExecutionContext,
+    MemberData::IsString,
+    MemberData::IsObject,
+    MemberData::IsFunctionObject,
+    MemberData::IsErrorObject,
+    MemberData::IsArrayData,
+    0,
+    MemberData::MyType,
+    "MemberData",
+    destroy,
+    markObjects,
+    isEqualTo
+};
+
+
+
+void MemberData::markObjects(Managed *that, ExecutionEngine *e)
+{
+    MemberData *m = static_cast<MemberData *>(that);
+    for (uint i = 0; i < m->size; ++i)
+        m->data[i].mark(e);
+}
+
+void Members::ensureIndex(QV4::ExecutionEngine *e, uint idx)
+{
+    uint s = size();
+    if (idx >= s) {
+        int newAlloc = qMax((uint)4, 2*idx);
+        uint alloc = sizeof(MemberData) + (newAlloc)*sizeof(Value);
+        MemberData *newMemberData = reinterpret_cast<MemberData *>(e->memoryManager->allocManaged(alloc));
+        if (d())
+            memcpy(newMemberData, d(), sizeof(MemberData) + s*sizeof(Value));
+        else
+            new (newMemberData) MemberData(e->memberDataClass);
+        newMemberData->size = newAlloc;
+        m = newMemberData;
+    }
+}
diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h
new file mode 100644 (file)
index 0000000..03aa75a
--- /dev/null
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4MEMBERDATA_H
+#define QV4MEMBERDATA_H
+
+#include "qv4global_p.h"
+#include "qv4managed_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct MemberData : Managed
+{
+    V4_MANAGED
+    uint size;
+    Value data[1];
+
+    MemberData(QV4::InternalClass *ic) : Managed(ic) {}
+    Value &operator[] (uint idx) { return data[idx]; }
+
+    static void markObjects(Managed *that, ExecutionEngine *e);
+};
+
+struct Members : Value
+{
+    void ensureIndex(QV4::ExecutionEngine *e, uint idx);
+    Value &operator[] (uint idx) const { return static_cast<MemberData *>(managed())->data[idx]; }
+    inline uint size() const { return d() ? d()->size : 0; }
+    inline MemberData *d() const { return static_cast<MemberData *>(managed()); }
+    Value *data() const { return static_cast<MemberData *>(managed())->data; }
+
+    void mark(ExecutionEngine *e) const {
+        MemberData *m = d();
+        if (m)
+            m->mark(e);
+    }
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
index 37dc0a8..c8d360d 100644 (file)
@@ -48,6 +48,7 @@
 #include "qv4mm_p.h"
 #include "qv4lookup_p.h"
 #include "qv4scopedvalue_p.h"
+#include "qv4memberdata_p.h"
 
 #include <private/qqmljsengine_p.h>
 #include <private/qqmljslexer_p.h>
@@ -71,26 +72,24 @@ DEFINE_OBJECT_VTABLE(Object);
 
 Object::Object(ExecutionEngine *engine)
     : Managed(engine->objectClass)
-    , memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
 {
 }
 
 Object::Object(InternalClass *ic)
     : Managed(ic)
-    , memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
 {
     Q_ASSERT(internalClass->vtable && internalClass->vtable != &Managed::static_vtbl);
 
-    if (internalClass->size >= memberDataAlloc) {
-        memberDataAlloc = internalClass->size;
-        memberData = new Value[memberDataAlloc];
+    Q_ASSERT(!memberData.d());
+    if (internalClass->size) {
+        Scope scope(engine());
+        ScopedObject protectThis(scope, this);
+        memberData.ensureIndex(engine(), internalClass->size);
     }
 }
 
 Object::~Object()
 {
-    if (memberData != inlineProperties)
-        delete [] memberData;
     _data = 0;
 }
 
@@ -222,24 +221,14 @@ void Object::markObjects(Managed *that, ExecutionEngine *e)
 {
     Object *o = static_cast<Object *>(that);
 
-    for (uint i = 0; i < o->internalClass->size; ++i)
-        o->memberData[i].mark(e);
+    o->memberData.mark(e);
     if (o->arrayData)
         o->arrayData->mark(e);
 }
 
 void Object::ensureMemberIndex(uint idx)
 {
-    if (idx >= memberDataAlloc) {
-        int newAlloc = qMax((uint)8, 2*memberDataAlloc);
-        Value *newMemberData = new Value[newAlloc];
-        memcpy(newMemberData, memberData, sizeof(Value)*memberDataAlloc);
-        memset(newMemberData + memberDataAlloc, 0, sizeof(Value)*(newAlloc - memberDataAlloc));
-        memberDataAlloc = newAlloc;
-        if (memberData != inlineProperties)
-            delete [] memberData;
-        memberData = newMemberData;
-    }
+    memberData.ensureIndex(engine(), idx);
 }
 
 void Object::insertMember(const StringRef s, const Property &p, PropertyAttributes attributes)
index 89dbde5..0dfaffc 100644 (file)
@@ -50,6 +50,7 @@
 #include "qv4property_p.h"
 #include "qv4internalclass_p.h"
 #include "qv4arraydata_p.h"
+#include "qv4memberdata_p.h"
 
 #include <QtCore/QString>
 #include <QtCore/QHash>
@@ -107,17 +108,11 @@ struct Q_QML_EXPORT Object: Managed {
     enum {
         IsObject = true
     };
-    uint memberDataAlloc;
-    Value *memberData;
+    Members memberData;
 
     ArrayData *arrayData;
 
-    enum {
-        InlinePropertySize = 4
-    };
-    Value inlineProperties[InlinePropertySize];
-
-    Property *propertyAt(uint index) const { return reinterpret_cast<Property *>(memberData + index); }
+    Property *propertyAt(uint index) const { return reinterpret_cast<Property *>(memberData.data() + index); }
 
     Object(ExecutionEngine *engine);
     Object(InternalClass *internalClass);