From 8ffb79bbd214c239e414dc4e9cf4569b3219bdab Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 12 Jan 2015 21:55:51 +0100 Subject: Refactor persistent values Use a page wise allocation mechanism for persistent values. This significantly reduces memory consumption of persistent values and also improves their performance a lot. Change-Id: I8499d2ca5bdd871e029f643ae605a94544558bb5 Reviewed-by: Simon Hausmann --- src/qml/jsapi/qjsengine.cpp | 105 +++++----- src/qml/jsapi/qjsvalue.cpp | 341 +++++++++++++++++++------------ src/qml/jsapi/qjsvalue_p.h | 52 ++++- src/qml/jsapi/qjsvalueiterator.cpp | 19 +- src/qml/jsruntime/qv4engine.cpp | 6 +- src/qml/jsruntime/qv4global_p.h | 2 +- src/qml/jsruntime/qv4mm.cpp | 70 ++----- src/qml/jsruntime/qv4mm_p.h | 10 +- src/qml/jsruntime/qv4persistent.cpp | 386 +++++++++++++++++++++-------------- src/qml/jsruntime/qv4persistent_p.h | 120 ++++++----- src/qml/jsruntime/qv4scopedvalue_p.h | 6 +- src/qml/jsruntime/qv4value_p.h | 1 - src/qml/qml/qqmltypewrapper.cpp | 2 +- src/qml/qml/v8/qv8engine.cpp | 9 +- 14 files changed, 659 insertions(+), 470 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 831287d0b3..d54af28f26 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -421,11 +421,18 @@ QJSValue QJSEngine::create(int type, const void *ptr) bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) { QJSValuePrivate *vp = QJSValuePrivate::get(value); - if (vp->engine) { - QV4::Scope scope(vp->engine); - QV4::ScopedValue v(scope, vp->getValue(scope.engine)); + QV4::ExecutionEngine *v4 = vp->engine(); + QV4::Value scratch; + QV4::Value *val = vp->valueForData(&scratch); + if (v4) { + QV4::Scope scope(v4); + QV4::ScopedValue v(scope, *val); return scope.engine->metaTypeFromJS(v, type, ptr); - } else if (vp->value.isEmpty()) { + } + + Q_ASSERT(vp->persistent.isEmpty()); + + if (!val) { if (vp->unboundData.userType() == QMetaType::QString) { QString string = vp->unboundData.toString(); // have a string based value without engine. Do conversion manually @@ -478,50 +485,52 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) } else { return QMetaType::convert(&vp->unboundData.data_ptr(), vp->unboundData.userType(), ptr, type); } - } else { - switch (type) { - case QMetaType::Bool: - *reinterpret_cast(ptr) = vp->value.toBoolean(); - return true; - case QMetaType::Int: - *reinterpret_cast(ptr) = vp->value.toInt32(); - return true; - case QMetaType::UInt: - *reinterpret_cast(ptr) = vp->value.toUInt32(); - return true; - case QMetaType::LongLong: - *reinterpret_cast(ptr) = vp->value.toInteger(); - return true; - case QMetaType::ULongLong: - *reinterpret_cast(ptr) = vp->value.toInteger(); - return true; - case QMetaType::Double: - *reinterpret_cast(ptr) = vp->value.toNumber(); - return true; - case QMetaType::QString: - *reinterpret_cast(ptr) = value.toString(); - return true; - case QMetaType::Float: - *reinterpret_cast(ptr) = vp->value.toNumber(); - return true; - case QMetaType::Short: - *reinterpret_cast(ptr) = vp->value.toInt32(); - return true; - case QMetaType::UShort: - *reinterpret_cast(ptr) = vp->value.toUInt16(); - return true; - case QMetaType::Char: - *reinterpret_cast(ptr) = vp->value.toInt32(); - return true; - case QMetaType::UChar: - *reinterpret_cast(ptr) = vp->value.toUInt16(); - return true; - case QMetaType::QChar: - *reinterpret_cast(ptr) = vp->value.toUInt16(); - return true; - default: - return false; - } + } + + Q_ASSERT(val); + + switch (type) { + case QMetaType::Bool: + *reinterpret_cast(ptr) = val->toBoolean(); + return true; + case QMetaType::Int: + *reinterpret_cast(ptr) = val->toInt32(); + return true; + case QMetaType::UInt: + *reinterpret_cast(ptr) = val->toUInt32(); + return true; + case QMetaType::LongLong: + *reinterpret_cast(ptr) = val->toInteger(); + return true; + case QMetaType::ULongLong: + *reinterpret_cast(ptr) = val->toInteger(); + return true; + case QMetaType::Double: + *reinterpret_cast(ptr) = val->toNumber(); + return true; + case QMetaType::QString: + *reinterpret_cast(ptr) = val->toQStringNoThrow(); + return true; + case QMetaType::Float: + *reinterpret_cast(ptr) = val->toNumber(); + return true; + case QMetaType::Short: + *reinterpret_cast(ptr) = val->toInt32(); + return true; + case QMetaType::UShort: + *reinterpret_cast(ptr) = val->toUInt16(); + return true; + case QMetaType::Char: + *reinterpret_cast(ptr) = val->toInt32(); + return true; + case QMetaType::UChar: + *reinterpret_cast(ptr) = val->toUInt16(); + return true; + case QMetaType::QChar: + *reinterpret_cast(ptr) = val->toUInt16(); + return true; + default: + return false; } } diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index d888ea3535..a158d3ad72 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -50,24 +50,50 @@ QV4::ReturnedValue QJSValuePrivate::getValue(QV4::ExecutionEngine *e) { - if (!this->engine) { - this->engine = e; - } else if (this->engine != e) { + QV4::ExecutionEngine *engine = persistent.engine(); + if (!engine) { + persistent = QV4::PersistentValue(e, e->fromVariant(unboundData)); + unboundData.clear(); + engine = e; + } + + if (engine != e) { qWarning("JSValue can't be reassigned to another engine."); return QV4::Encode::undefined(); } - if (value.isEmpty()) { - value = QV4::Encode(e->fromVariant(unboundData)); - PersistentValuePrivate **listRoot = &engine->memoryManager->m_persistentValues; - prev = listRoot; - next = *listRoot; - *prev = this; - if (next) - next->prev = &this->next; - unboundData.clear(); + return persistent.value(); +} + +QV4::Value QJSValuePrivate::primitiveValueForUnboundData() const +{ + QV4::Value v = QV4::Primitive::emptyValue(); + switch ((int)unboundData.type()) { + case QMetaType::UnknownType: + case QMetaType::Void: + v = QV4::Encode::undefined(); + break; + case QMetaType::VoidStar: + v = QV4::Encode::null(); + break; + case QMetaType::Bool: + v = QV4::Encode(unboundData.toBool()); + break; + case QMetaType::Double: + v = QV4::Encode(unboundData.toDouble()); + break; + case QMetaType::Int: + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::Char: + case QMetaType::UChar: + v = QV4::Encode(unboundData.toInt()); + break; + case QMetaType::UInt: + v = QV4::Encode(unboundData.toUInt()); + break; } - return value.asReturnedValue(); + return v; } /*! @@ -142,7 +168,7 @@ using namespace QV4; Constructs a new QJSValue with a boolean \a value. */ QJSValue::QJSValue(bool value) - : d(new QJSValuePrivate(Encode(value))) + : d(new QJSValuePrivate(QVariant(value))) { } @@ -155,7 +181,7 @@ QJSValue::QJSValue(QJSValuePrivate *dd) Constructs a new QJSValue with a number \a value. */ QJSValue::QJSValue(int value) - : d(new QJSValuePrivate(Encode(value))) + : d(new QJSValuePrivate(QVariant((double)value))) { } @@ -163,7 +189,7 @@ QJSValue::QJSValue(int value) Constructs a new QJSValue with a number \a value. */ QJSValue::QJSValue(uint value) - : d(new QJSValuePrivate(Encode(value))) + : d(new QJSValuePrivate(QVariant((double)value))) { } @@ -171,7 +197,7 @@ QJSValue::QJSValue(uint value) Constructs a new QJSValue with a number \a value. */ QJSValue::QJSValue(double value) - : d(new QJSValuePrivate(Encode(value))) + : d(new QJSValuePrivate(QVariant(value))) { } @@ -187,8 +213,10 @@ QJSValue::QJSValue(const QString& value) Constructs a new QJSValue with a special \a value. */ QJSValue::QJSValue(SpecialValue value) - : d(new QJSValuePrivate(value == UndefinedValue ? Encode::undefined() : Encode::null())) + : d(new QJSValuePrivate) { + if (value == NullValue) + d->unboundData = QVariant(QMetaType::VoidStar, (void *)0); } /*! @@ -238,7 +266,9 @@ QJSValue::~QJSValue() */ bool QJSValue::isBool() const { - return d->value.isBoolean(); + if (d->persistent.valueRef()) + return d->persistent.valueRef()->isBoolean(); + return d->unboundData.type() == QVariant::Bool; } /*! @@ -249,7 +279,9 @@ bool QJSValue::isBool() const */ bool QJSValue::isNumber() const { - return d->value.isNumber(); + if (d->persistent.valueRef()) + return d->persistent.valueRef()->isNumber(); + return d->unboundData.type() == QVariant::Double; } /*! @@ -258,7 +290,9 @@ bool QJSValue::isNumber() const */ bool QJSValue::isNull() const { - return d->value.isNull(); + if (d->persistent.valueRef()) + return d->persistent.valueRef()->isNull(); + return (int)d->unboundData.type() == QMetaType::VoidStar; } /*! @@ -269,7 +303,9 @@ bool QJSValue::isNull() const */ bool QJSValue::isString() const { - return d->value.isEmpty() || d->value.isString(); + if (d->persistent.valueRef()) + return d->persistent.valueRef()->isString(); + return (int)d->unboundData.type() == QMetaType::QString; } /*! @@ -278,7 +314,9 @@ bool QJSValue::isString() const */ bool QJSValue::isUndefined() const { - return d->value.isUndefined(); + if (d->persistent.valueRef()) + return d->persistent.valueRef()->isUndefined(); + return (int)d->unboundData.type() == QMetaType::UnknownType || (int)d->unboundData.type() == QMetaType::Void; } /*! @@ -287,7 +325,9 @@ bool QJSValue::isUndefined() const */ bool QJSValue::isError() const { - Object *o = d->value.asObject(); + if (d->persistent.isEmpty()) + return false; + Object *o = d->persistent.valueRef()->asObject(); return o && o->asErrorObject(); } @@ -299,7 +339,9 @@ bool QJSValue::isError() const */ bool QJSValue::isArray() const { - return d->value.asArrayObject(); + if (d->persistent.isEmpty()) + return false; + return d->persistent.valueRef()->asArrayObject(); } /*! @@ -313,7 +355,9 @@ bool QJSValue::isArray() const */ bool QJSValue::isObject() const { - return d->value.asObject(); + if (d->persistent.isEmpty()) + return false; + return d->persistent.valueRef()->asObject(); } /*! @@ -324,7 +368,9 @@ bool QJSValue::isObject() const */ bool QJSValue::isCallable() const { - return d->value.asFunctionObject(); + if (d->persistent.isEmpty()) + return false; + return d->persistent.valueRef()->asFunctionObject(); } /*! @@ -335,8 +381,9 @@ bool QJSValue::isCallable() const */ bool QJSValue::isVariant() const { - Managed *m = d->value.asManaged(); - return m ? m->as() : 0; + if (d->persistent.isEmpty()) + return false; + return d->persistent.valueRef()->as(); } /*! @@ -353,7 +400,10 @@ bool QJSValue::isVariant() const */ QString QJSValue::toString() const { - if (d->value.isEmpty()) { + QV4::Value scratch; + QV4::Value *val = d->valueForData(&scratch); + + if (!val) { if (d->unboundData.type() == QVariant::Map) return QStringLiteral("[object Object]"); else if (d->unboundData.type() == QVariant::List) { @@ -368,7 +418,7 @@ QString QJSValue::toString() const } return d->unboundData.toString(); } - return d->value.toQStringNoThrow(); + return val->toQStringNoThrow(); } /*! @@ -385,7 +435,10 @@ QString QJSValue::toString() const */ double QJSValue::toNumber() const { - if (d->value.isEmpty()) { + QV4::Value scratch; + QV4::Value *val = d->valueForData(&scratch); + + if (!val) { if (d->unboundData.type() == QVariant::String) return RuntimeHelpers::stringToNumber(d->unboundData.toString()); else if (d->unboundData.canConvert()) @@ -394,9 +447,10 @@ double QJSValue::toNumber() const return std::numeric_limits::quiet_NaN(); } - double dbl = d->value.toNumber(); - if (d->engine && d->engine->hasException) { - d->engine->catchException(); + double dbl = val->toNumber(); + QV4::ExecutionEngine *engine = d->persistent.engine(); + if (engine && engine->hasException) { + engine->catchException(); return 0; } return dbl; @@ -416,16 +470,20 @@ double QJSValue::toNumber() const */ bool QJSValue::toBool() const { - if (d->value.isEmpty()) { + QV4::Value scratch; + QV4::Value *val = d->valueForData(&scratch); + + if (!val) { if (d->unboundData.userType() == QMetaType::QString) return d->unboundData.toString().length() > 0; else return d->unboundData.toBool(); } - bool b = d->value.toBoolean(); - if (d->engine && d->engine->hasException) { - d->engine->catchException(); + bool b = val->toBoolean(); + QV4::ExecutionEngine *engine = d->persistent.engine(); + if (engine && engine->hasException) { + engine->catchException(); return false; } return b; @@ -445,16 +503,20 @@ bool QJSValue::toBool() const */ qint32 QJSValue::toInt() const { - if (d->value.isEmpty()) { + QV4::Value scratch; + QV4::Value *val = d->valueForData(&scratch); + + if (!val) { if (d->unboundData.userType() == QMetaType::QString) return QV4::Primitive::toInt32(RuntimeHelpers::stringToNumber(d->unboundData.toString())); else return d->unboundData.toInt(); } - qint32 i = d->value.toInt32(); - if (d->engine && d->engine->hasException) { - d->engine->catchException(); + qint32 i = val->toInt32(); + QV4::ExecutionEngine *engine = d->persistent.engine(); + if (engine && engine->hasException) { + engine->catchException(); return 0; } return i; @@ -474,16 +536,20 @@ qint32 QJSValue::toInt() const */ quint32 QJSValue::toUInt() const { - if (d->value.isEmpty()) { + QV4::Value scratch; + QV4::Value *val = d->valueForData(&scratch); + + if (!val) { if (d->unboundData.userType() == QMetaType::QString) return QV4::Primitive::toUInt32(RuntimeHelpers::stringToNumber(d->unboundData.toString())); else return d->unboundData.toUInt(); } - quint32 u = d->value.toUInt32(); - if (d->engine && d->engine->hasException) { - d->engine->catchException(); + quint32 u = val->toUInt32(); + QV4::ExecutionEngine *engine = d->persistent.engine(); + if (engine && engine->hasException) { + engine->catchException(); return 0; } return u; @@ -513,24 +579,24 @@ quint32 QJSValue::toUInt() const */ QVariant QJSValue::toVariant() const { - if (d->value.isEmpty()) + if (d->persistent.isEmpty()) return d->unboundData; - if (Object *o = d->value.asObject()) - return o->engine()->toVariant(d->value, /*typeHint*/ -1, /*createJSValueForObjects*/ false); - - if (d->value.isString()) - return QVariant(d->value.stringValue()->toQString()); - if (d->value.isBoolean()) - return QVariant(d->value.booleanValue()); - if (d->value.isNumber()) { - if (d->value.isInt32()) - return QVariant(d->value.integerValue()); - return QVariant(d->value.asDouble()); + if (Object *o = d->persistent.valueRef()->asObject()) + return o->engine()->toVariant(*d->persistent.valueRef(), /*typeHint*/ -1, /*createJSValueForObjects*/ false); + + if (d->persistent.valueRef()->isString()) + return QVariant(d->persistent.valueRef()->stringValue()->toQString()); + if (d->persistent.valueRef()->isBoolean()) + return QVariant(d->persistent.valueRef()->booleanValue()); + if (d->persistent.valueRef()->isNumber()) { + if (d->persistent.valueRef()->isInt32()) + return QVariant(d->persistent.valueRef()->integerValue()); + return QVariant(d->persistent.valueRef()->asDouble()); } - if (d->value.isNull()) + if (d->persistent.valueRef()->isNull()) return QVariant(QMetaType::VoidStar, 0); - Q_ASSERT(d->value.isUndefined()); + Q_ASSERT(d->persistent.valueRef()->isUndefined()); return QVariant(); } @@ -551,11 +617,14 @@ QVariant QJSValue::toVariant() const */ QJSValue QJSValue::call(const QJSValueList &args) { - FunctionObject *f = d->value.asFunctionObject(); + if (d->persistent.isEmpty()) + return QJSValue(); + + FunctionObject *f = d->persistent.valueRef()->asFunctionObject(); if (!f) return QJSValue(); - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->persistent.engine(); Q_ASSERT(engine); Scope scope(engine); @@ -570,8 +639,8 @@ QJSValue QJSValue::call(const QJSValueList &args) } ScopedValue result(scope, f->call(callData)); - if (d->engine->hasException) - result = d->engine->catchException(); + if (engine->hasException) + result = engine->catchException(); return new QJSValuePrivate(engine, result); } @@ -598,11 +667,14 @@ QJSValue QJSValue::call(const QJSValueList &args) */ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList &args) { - FunctionObject *f = d->value.asFunctionObject(); + if (d->persistent.isEmpty()) + return QJSValue(); + + FunctionObject *f = d->persistent.valueRef()->asFunctionObject(); if (!f) return QJSValue(); - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->persistent.engine(); Q_ASSERT(engine); Scope scope(engine); @@ -622,8 +694,8 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList } ScopedValue result(scope, f->call(callData)); - if (d->engine->hasException) - result = d->engine->catchException(); + if (engine->hasException) + result = engine->catchException(); return new QJSValuePrivate(engine, result); } @@ -648,11 +720,14 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList */ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) { - FunctionObject *f = d->value.asFunctionObject(); + if (d->persistent.isEmpty()) + return QJSValue(); + + FunctionObject *f = d->persistent.valueRef()->asFunctionObject(); if (!f) return QJSValue(); - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->persistent.engine(); Q_ASSERT(engine); Scope scope(engine); @@ -666,8 +741,8 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) } ScopedValue result(scope, f->construct(callData)); - if (d->engine->hasException) - result = d->engine->catchException(); + if (engine->hasException) + result = engine->catchException(); return new QJSValuePrivate(engine, result); } @@ -683,7 +758,7 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) */ QJSEngine* QJSValue::engine() const { - QV4::ExecutionEngine *engine = d->engine; + QV4::ExecutionEngine *engine = d->persistent.engine(); if (engine) return engine->jsEngine(); return 0; @@ -700,11 +775,11 @@ QJSEngine* QJSValue::engine() const */ QJSValue QJSValue::prototype() const { - QV4::ExecutionEngine *engine = d->engine; + QV4::ExecutionEngine *engine = d->persistent.engine(); if (!engine) return QJSValue(); QV4::Scope scope(engine); - ScopedObject o(scope, d->value.asObject()); + ScopedObject o(scope, d->persistent.valueRef()->asObject()); if (!o) return QJSValue(); ScopedObject p(scope, o->prototype()); @@ -727,19 +802,20 @@ QJSValue QJSValue::prototype() const */ void QJSValue::setPrototype(const QJSValue& prototype) { - ExecutionEngine *v4 = d->engine; + ExecutionEngine *v4 = d->persistent.engine(); if (!v4) return; Scope scope(v4); - ScopedObject o(scope, d->value); + ScopedObject o(scope, d->persistent.value()); if (!o) return; - if (prototype.d->value.isNull()) { + prototype.d->getValue(v4); + if (prototype.d->persistent.valueRef()->isNull()) { o->setPrototype(0); return; } - ScopedObject p(scope, prototype.d->value); + ScopedObject p(scope, prototype.d->persistent.value()); if (!p) return; if (o->engine() != p->engine()) { @@ -809,17 +885,21 @@ static bool js_equal(const QString &string, QV4::ValueRef value) */ bool QJSValue::equals(const QJSValue& other) const { - if (d->value.isEmpty()) { - if (other.d->value.isEmpty()) + QV4::Value s1, s2; + QV4::Value *v = d->valueForData(&s1); + QV4::Value *ov = other.d->valueForData(&s2); + + if (!v) { + if (!ov) return d->unboundData == other.d->unboundData; if (d->unboundData.type() == QVariant::Map || d->unboundData.type() == QVariant::List) return false; - return js_equal(d->unboundData.toString(), QV4::ValueRef(other.d->value)); - } - if (other.d->value.isEmpty()) + return js_equal(d->unboundData.toString(), *ov); + } + if (!ov) return other.equals(*this); - return Runtime::compareEqual(QV4::ValueRef(d), QV4::ValueRef(other.d)); + return Runtime::compareEqual(*v, *ov); } /*! @@ -846,19 +926,23 @@ bool QJSValue::equals(const QJSValue& other) const */ bool QJSValue::strictlyEquals(const QJSValue& other) const { - if (d->value.isEmpty()) { - if (other.d->value.isEmpty()) + QV4::Value s1, s2; + QV4::Value *v = d->valueForData(&s1); + QV4::Value *ov = other.d->valueForData(&s2); + + if (!v) { + if (!ov) return d->unboundData == other.d->unboundData; if (d->unboundData.type() == QVariant::Map || d->unboundData.type() == QVariant::List) return false; - if (other.d->value.isString()) - return d->unboundData.toString() == other.d->value.stringValue()->toQString(); + if (ov->isString()) + return d->unboundData.toString() == ov->stringValue()->toQString(); return false; } - if (other.d->value.isEmpty()) + if (!ov) return other.strictlyEquals(*this); - return RuntimeHelpers::strictEqual(QV4::ValueRef(d), QV4::ValueRef(other.d)); + return RuntimeHelpers::strictEqual(*v, *ov); } /*! @@ -876,12 +960,12 @@ bool QJSValue::strictlyEquals(const QJSValue& other) const */ QJSValue QJSValue::property(const QString& name) const { - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->engine(); if (!engine) return QJSValue(); - QV4::Scope scope(engine); - ScopedObject o(scope, d->value); + QV4::Scope scope(engine); + ScopedObject o(scope, d->persistent.value()); if (!o) return QJSValue(); @@ -892,8 +976,8 @@ QJSValue QJSValue::property(const QString& name) const s->makeIdentifier(); QV4::ScopedValue result(scope, o->get(s)); - if (d->engine->hasException) - result = d->engine->catchException(); + if (engine->hasException) + result = engine->catchException(); return new QJSValuePrivate(engine, result); } @@ -912,18 +996,18 @@ QJSValue QJSValue::property(const QString& name) const */ QJSValue QJSValue::property(quint32 arrayIndex) const { - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->engine(); if (!engine) return QJSValue(); QV4::Scope scope(engine); - ScopedObject o(scope, d->value); + ScopedObject o(scope, d->persistent.value()); if (!o) return QJSValue(); QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax) : o->getIndexed(arrayIndex)); - if (d->engine->hasException) - d->engine->catchException(); + if (engine->hasException) + engine->catchException(); return new QJSValuePrivate(engine, result); } @@ -940,12 +1024,12 @@ QJSValue QJSValue::property(quint32 arrayIndex) const */ void QJSValue::setProperty(const QString& name, const QJSValue& value) { - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->engine(); if (!engine) return; Scope scope(engine); - ScopedObject o(scope, d->value); + ScopedObject o(scope, d->persistent.value()); if (!o) return; @@ -964,8 +1048,8 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) s->makeIdentifier(); QV4::ScopedValue v(scope, value.d->getValue(engine)); o->put(s, v); - if (d->engine->hasException) - d->engine->catchException(); + if (engine->hasException) + engine->catchException(); } /*! @@ -982,12 +1066,12 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) */ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) { - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->engine(); if (!engine) return; Scope scope(engine); - ScopedObject o(scope, d->value); + ScopedObject o(scope, d->persistent.value()); if (!o) return; @@ -996,8 +1080,8 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) o->putIndexed(arrayIndex, v); else o->put(engine->id_uintMax, v); - if (d->engine->hasException) - d->engine->catchException(); + if (engine->hasException) + engine->catchException(); } /*! @@ -1022,16 +1106,16 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) */ bool QJSValue::deleteProperty(const QString &name) { - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->engine(); Scope scope(engine); - ScopedObject o(scope, d->value.asObject()); + ScopedObject o(scope, d->persistent.value()); if (!o) return false; ScopedString s(scope, engine->newString(name)); bool b = o->deleteProperty(s); - if (d->engine->hasException) - d->engine->catchException(); + if (engine->hasException) + engine->catchException(); return b; } @@ -1043,12 +1127,12 @@ bool QJSValue::deleteProperty(const QString &name) */ bool QJSValue::hasProperty(const QString &name) const { - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->engine(); if (!engine) return false; Scope scope(engine); - ScopedObject o(scope, d->value); + ScopedObject o(scope, d->persistent.value()); if (!o) return false; @@ -1064,12 +1148,12 @@ bool QJSValue::hasProperty(const QString &name) const */ bool QJSValue::hasOwnProperty(const QString &name) const { - ExecutionEngine *engine = d->engine; + ExecutionEngine *engine = d->engine(); if (!engine) return false; Scope scope(engine); - ScopedObject o(scope, d->value); + ScopedObject o(scope, d->persistent.value()); if (!o) return false; @@ -1089,10 +1173,11 @@ bool QJSValue::hasOwnProperty(const QString &name) const */ QObject *QJSValue::toQObject() const { - if (!d->engine) + ExecutionEngine *engine = d->engine(); + if (!engine) return 0; - QV4::Scope scope(d->engine); - QV4::Scoped wrapper(scope, d->value); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, d->persistent.value()); if (!wrapper) return 0; @@ -1108,10 +1193,12 @@ QObject *QJSValue::toQObject() const */ QDateTime QJSValue::toDateTime() const { - QV4::DateObject *date = d->value.asDateObject(); - if (!date) - return QDateTime(); - return date->toQDateTime(); + if (!d->persistent.isEmpty()) { + QV4::DateObject *date = d->persistent.valueRef()->asDateObject(); + if (date) + return date->toQDateTime(); + } + return QDateTime(); } /*! @@ -1120,7 +1207,7 @@ QDateTime QJSValue::toDateTime() const */ bool QJSValue::isDate() const { - return d->value.asDateObject(); + return !d->persistent.isEmpty() && d->persistent.valueRef()->asDateObject(); } /*! @@ -1129,7 +1216,7 @@ bool QJSValue::isDate() const */ bool QJSValue::isRegExp() const { - return d->value.as(); + return !d->persistent.isEmpty() && d->persistent.valueRef()->as(); } /*! @@ -1143,7 +1230,7 @@ bool QJSValue::isRegExp() const */ bool QJSValue::isQObject() const { - return d->value.as() != 0; + return !d->persistent.isEmpty() && d->persistent.valueRef()->as() != 0; } QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 43a3a74e38..3d7afdbd75 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -52,6 +52,8 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -59,30 +61,60 @@ QT_BEGIN_NAMESPACE \internal \class QJSValuePrivate */ -class Q_QML_PRIVATE_EXPORT QJSValuePrivate : public QV4::PersistentValuePrivate +class Q_QML_PRIVATE_EXPORT QJSValuePrivate { public: - QJSValuePrivate(QV4::ExecutionEngine *engine, const QV4::ValueRef v) - : PersistentValuePrivate(v.asReturnedValue(), engine) + QJSValuePrivate() + : refcount(1) + {} + QJSValuePrivate(QV4::ExecutionEngine *e, const QV4::ValueRef v) + : refcount(1), + persistent(e, v) { - Q_ASSERT(!value.isEmpty()); + Q_ASSERT(!v->isEmpty()); } - QJSValuePrivate(QV4::ReturnedValue v) - : PersistentValuePrivate(v) + QJSValuePrivate(QV4::ExecutionEngine *e, QV4::ReturnedValue v) + : refcount(1), + persistent(e, v) { - Q_ASSERT(!value.isEmpty()); } - QJSValuePrivate(const QString &s) - : PersistentValuePrivate(QV4::Primitive::emptyValue().asReturnedValue()), - unboundData(s) + explicit QJSValuePrivate(const QVariant &v) + : refcount(1), + unboundData(v) { } + bool checkEngine(QV4::ExecutionEngine *e) { + if (persistent.isEmpty()) + getValue(e); + return persistent.engine() == e; + } + QV4::ReturnedValue getValue(QV4::ExecutionEngine *e); static QJSValuePrivate *get(const QJSValue &v) { return v.d; } + QV4::ExecutionEngine *engine() const { return persistent.engine(); } + + void ref() { ++refcount; } + void deref() { + if (!--refcount) + delete this; + } + + QV4::Value *valueForData(QV4::Value *scratch) const { + QV4::Value *v = persistent.valueRef(); + if (v) + return v; + *scratch = primitiveValueForUnboundData(); + return (scratch->isEmpty() ? 0 : scratch); + } + QV4::Value primitiveValueForUnboundData() const; + int refcount; + QV4::PersistentValue persistent; QVariant unboundData; +private: + QJSValuePrivate(QV4::ReturnedValue v) Q_DECL_EQ_DELETE; }; QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp index c74a5d2d76..e23bd5d763 100644 --- a/src/qml/jsapi/qjsvalueiterator.cpp +++ b/src/qml/jsapi/qjsvalueiterator.cpp @@ -46,12 +46,12 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v) , nextIndex(UINT_MAX) { QJSValuePrivate *jsp = QJSValuePrivate::get(value); - QV4::ExecutionEngine *e = jsp->engine; + QV4::ExecutionEngine *e = jsp->persistent.engine(); if (!e) return; QV4::Scope scope(e); - QV4::ScopedObject o(scope, jsp->value); + QV4::ScopedObject o(scope, jsp->persistent.value()); iterator.set(e, e->newForEachIteratorObject(o)); currentName = (QV4::String *)0; @@ -123,7 +123,8 @@ QJSValueIterator::~QJSValueIterator() */ bool QJSValueIterator::hasNext() const { - if (!QJSValuePrivate::get(d_ptr->value)->value.isObject()) + QV4::Value *val = QJSValuePrivate::get(d_ptr->value)->persistent.valueRef(); + if (!val || !val->isObject()) return false; return !!d_ptr->nextName || d_ptr->nextIndex != UINT_MAX; } @@ -138,7 +139,8 @@ bool QJSValueIterator::hasNext() const */ bool QJSValueIterator::next() { - if (!QJSValuePrivate::get(d_ptr->value)->value.isObject()) + QV4::Value *val = QJSValuePrivate::get(d_ptr->value)->persistent.valueRef(); + if (!val || !val->isObject()) return false; d_ptr->currentName = d_ptr->nextName; d_ptr->currentIndex = d_ptr->nextIndex; @@ -164,7 +166,8 @@ bool QJSValueIterator::next() */ QString QJSValueIterator::name() const { - if (!QJSValuePrivate::get(d_ptr->value)->value.isObject()) + QV4::Value *val = QJSValuePrivate::get(d_ptr->value)->persistent.valueRef(); + if (!val || !val->isObject()) return QString(); if (!!d_ptr->currentName) return d_ptr->currentName->toQString(); @@ -186,7 +189,7 @@ QJSValue QJSValueIterator::value() const if (!engine) return QJSValue(); QV4::Scope scope(engine); - QV4::ScopedObject obj(scope, QJSValuePrivate::get(d_ptr->value)->value); + QV4::ScopedObject obj(scope, QJSValuePrivate::get(d_ptr->value)->persistent.value()); if (!obj) return QJSValue(); @@ -216,13 +219,13 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) d_ptr->nextName = (QV4::String *)0; QV4::ExecutionEngine *v4 = d_ptr->iterator.engine(); if (!v4) { - d_ptr->iterator.set(v4, QV4::Encode::undefined()); + d_ptr->iterator.clear(); return *this; } QJSValuePrivate *jsp = QJSValuePrivate::get(object); QV4::Scope scope(v4); - QV4::ScopedObject o(scope, jsp->value); + QV4::ScopedObject o(scope, jsp->persistent.value()); d_ptr->iterator.set(v4, v4->newForEachIteratorObject(o)); QV4::Scoped it(scope, d_ptr->iterator.value()); it->d()->it.flags = QV4::ObjectIterator::NoFlags; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index a51ea36351..3d3423b03a 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -183,7 +183,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) : current(0) - , memoryManager(new QV4::MemoryManager) + , memoryManager(new QV4::MemoryManager(this)) , executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) , bumperPointerAllocator(new WTF::BumpPointerAllocator) @@ -218,8 +218,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) } iselFactory.reset(factory); - memoryManager->setExecutionEngine(this); - // reserve space for the JS stack // we allow it to grow to 2 times JSStackLimit, as we can overshoot due to garbage collection // and ScopedValues allocated outside of JIT'ed methods. @@ -1291,7 +1289,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::ValueRef value, in return re->toQRegExp(); if (createJSValueForObjects) - return QVariant::fromValue(QJSValue(new QJSValuePrivate(o->asReturnedValue()))); + return QVariant::fromValue(QJSValue(new QJSValuePrivate(scope.engine, o->asReturnedValue()))); return objectToVariant(e, o, visitedObjects); } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 687ff19be4..5378cc92df 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -198,7 +198,7 @@ typedef Scoped ScopedArrayObject; typedef Scoped ScopedFunctionObject; typedef Scoped ScopedContext; -struct PersistentValuePrivate; +struct PersistentValueStorage; class PersistentValue; class WeakValue; diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp index c5538a8c7f..f4c3d930ac 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/jsruntime/qv4mm.cpp @@ -208,14 +208,15 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec } // namespace -MemoryManager::MemoryManager() +MemoryManager::MemoryManager(ExecutionEngine *engine) : m_d(new Data) - , m_persistentValues(0) - , m_weakValues(0) + , m_persistentValues(new PersistentValueStorage(engine)) + , m_weakValues(new PersistentValueStorage(engine)) { #ifdef V4_USE_VALGRIND VALGRIND_CREATE_MEMPOOL(this, 0, true); #endif + m_d->engine = engine; } Heap::Base *MemoryManager::allocData(std::size_t size) @@ -334,21 +335,7 @@ void MemoryManager::mark() m_d->engine->markObjects(); - PersistentValuePrivate *persistent = m_persistentValues; - while (persistent) { - if (!persistent->refcount) { - PersistentValuePrivate *n = persistent->next; - persistent->removeFromList(); - delete persistent; - persistent = n; - continue; - } - persistent->value.mark(m_d->engine); - persistent = persistent->next; - - if (m_d->engine->jsStackTop >= m_d->engine->jsStackLimit) - drainMarkStack(m_d->engine, markBase); - } + m_persistentValues->mark(m_d->engine); collectFromJSStack(); @@ -358,12 +345,12 @@ void MemoryManager::mark() // Do this _after_ collectFromStack to ensure that processing the weak // managed objects in the loop down there doesn't make then end up as leftovers // on the stack and thus always get collected. - for (PersistentValuePrivate *weak = m_weakValues; weak; weak = weak->next) { - if (!weak->refcount || !weak->value.isManaged()) + for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { + if (!(*it).isManaged()) continue; - if (weak->value.asManaged()->d()->gcGetInternalClass()->vtable != QObjectWrapper::staticVTable()) + if ((*it).managed()->d()->gcGetInternalClass()->vtable != QObjectWrapper::staticVTable()) continue; - QObjectWrapper *qobjectWrapper = static_cast(weak->value.managed()); + QObjectWrapper *qobjectWrapper = static_cast((*it).managed()); if (!qobjectWrapper) continue; QObject *qobject = qobjectWrapper->object(); @@ -392,25 +379,13 @@ void MemoryManager::mark() void MemoryManager::sweep(bool lastSweep) { - PersistentValuePrivate *weak = m_weakValues; - while (weak) { - if (!weak->refcount) { - PersistentValuePrivate *n = weak->next; - weak->removeFromList(); - delete weak; - weak = n; - continue; - } - if (Managed *m = weak->value.asManaged()) { - if (!m->markBit()) { - weak->value = Primitive::undefinedValue(); - PersistentValuePrivate *n = weak->next; - weak->removeFromList(); - weak = n; - continue; + if (m_weakValues) { + for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { + if (Managed *m = (*it).asManaged()) { + if (!m->markBit()) + (*it) = Primitive::undefinedValue(); } } - weak = weak->next; } if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = m_d->engine->m_multiplyWrappedQObjects) { @@ -580,15 +555,9 @@ size_t MemoryManager::getLargeItemsMem() const MemoryManager::~MemoryManager() { - PersistentValuePrivate *persistent = m_persistentValues; - while (persistent) { - PersistentValuePrivate *n = persistent->next; - persistent->value = Primitive::undefinedValue(); - persistent->engine = 0; - persistent->prev = 0; - persistent->next = 0; - persistent = n; - } + delete m_persistentValues; + delete m_weakValues; + m_weakValues = 0; sweep(/*lastSweep*/true); #ifdef V4_USE_VALGRIND @@ -601,11 +570,6 @@ ExecutionEngine *MemoryManager::engine() const return m_d->engine; } -void MemoryManager::setExecutionEngine(ExecutionEngine *engine) -{ - m_d->engine = engine; -} - void MemoryManager::dumpStats() const { #ifdef DETAILED_MM_STATS diff --git a/src/qml/jsruntime/qv4mm_p.h b/src/qml/jsruntime/qv4mm_p.h index 5c21294ad0..6049eb2d8a 100644 --- a/src/qml/jsruntime/qv4mm_p.h +++ b/src/qml/jsruntime/qv4mm_p.h @@ -48,8 +48,7 @@ struct GCDeletable; class Q_QML_EXPORT MemoryManager { - MemoryManager(const MemoryManager &); - MemoryManager &operator=(const MemoryManager&); + Q_DISABLE_COPY(MemoryManager); public: struct Data; @@ -75,7 +74,7 @@ public: }; public: - MemoryManager(); + MemoryManager(ExecutionEngine *engine); ~MemoryManager(); // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). @@ -149,7 +148,6 @@ public: void runGC(); ExecutionEngine *engine() const; - void setExecutionEngine(ExecutionEngine *engine); void dumpStats() const; @@ -176,8 +174,8 @@ private: protected: QScopedPointer m_d; public: - PersistentValuePrivate *m_persistentValues; - PersistentValuePrivate *m_weakValues; + PersistentValueStorage *m_persistentValues; + PersistentValueStorage *m_weakValues; }; } diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 1236d2a469..ff5061d968 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -34,232 +34,320 @@ #include "qv4persistent_p.h" #include "qv4mm_p.h" #include "qv4object_p.h" +#include "PageAllocation.h" using namespace QV4; -PersistentValue::PersistentValue(ExecutionEngine *engine, const ValueRef val) - : d(new PersistentValuePrivate(val.asReturnedValue(), engine)) +namespace { + +struct Page; +struct Header { + WTF::PageAllocation alloc; + ExecutionEngine *engine; + Page **prev; + Page *next; + int refCount; + int freeList; +}; + +enum { + PageSize = 4096, + NEntries = (PageSize - sizeof(Header))/sizeof(Value) +}; +struct Page { + Header header; + Value values[NEntries]; +}; + +Page *getPage(Value *val) { + return reinterpret_cast(reinterpret_cast(val) & ~((quintptr)(PageSize - 1))); +} + + +Page *allocatePage(PersistentValueStorage *storage) { + PageAllocation page = WTF::PageAllocation::allocate(PageSize); + Page *p = reinterpret_cast(page.base()); + + Q_ASSERT(!((quintptr)p & (PageSize - 1))); + + p->header.engine = storage->engine; + p->header.alloc = page; + p->header.next = reinterpret_cast(storage->firstPage); + p->header.prev = reinterpret_cast(&storage->firstPage); + p->header.refCount = 0; + p->header.freeList = 0; + if (p->header.next) + p->header.next->header.prev = &p->header.next; + for (int i = 0; i < NEntries - 1; ++i) { + p->values[i].tag = QV4::Value::Empty_Type; + p->values[i].int_32 = i + 1; + } + p->values[NEntries - 1].tag = QV4::Value::Empty_Type; + p->values[NEntries - 1].int_32 = -1; + + storage->firstPage = p; + + return p; +} + + +} + + +PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator++() { + while (p) { + while (index < NEntries - 1) { + ++index; + if (static_cast(p)->values[index].tag != QV4::Value::Empty_Type) + return *this; + } + index = -1; + p = static_cast(p)->header.next; + } + index = 0; + return *this; } -PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue val) - : d(new PersistentValuePrivate(val, engine)) +Value &PersistentValueStorage::Iterator::operator *() { + return static_cast(p)->values[index]; } -PersistentValue::PersistentValue(const PersistentValue &other) - : d(other.d) +PersistentValueStorage::PersistentValueStorage(ExecutionEngine *engine) + : engine(engine), + firstPage(0) { - if (d) - d->ref(); } -PersistentValue::~PersistentValue() +PersistentValueStorage::~PersistentValueStorage() { - if (d) - d->deref(); + Page *p = static_cast(firstPage); + while (p) { + for (int i = 0; i < NEntries; ++i) { + if (!p->values[i].isEmpty()) + p->values[i] = Encode::undefined(); + } + Page *n = p->header.next; + p->header.engine = 0; + p->header.prev = 0; + p->header.next = 0; + Q_ASSERT(p->header.refCount); + p = n; + } } -PersistentValue &PersistentValue::operator=(const PersistentValue &other) +Value *PersistentValueStorage::allocate() { - if (d == other.d) - return *this; + Page *p = static_cast(firstPage); + while (p) { + if (p->header.freeList != -1) + break; + p = p->header.next; + } + if (!p) + p = allocatePage(this); - // the memory manager cleans up those with a refcount of 0 + Value *v = p->values + p->header.freeList; + p->header.freeList = v->int_32; + ++p->header.refCount; - if (d) - d->deref(); - d = other.d; - if (d) - d->ref(); + v->val = Encode::undefined(); - return *this; + return v; } -PersistentValue &PersistentValue::operator=(const WeakValue &other) +void PersistentValueStorage::free(Value *v) { - QV4::ExecutionEngine *engine = other.engine(); - if (!d) - d = new PersistentValuePrivate(other.value(), engine); - else - d = d->detach(other.value()); - return *this; + if (!v) + return; + + Page *p = getPage(v); + + v->tag = QV4::Value::Empty_Type; + v->int_32 = p->header.freeList; + p->header.freeList = v - p->values; + if (!--p->header.refCount) { + if (p->header.prev) + *p->header.prev = p->header.next; + if (p->header.next) + p->header.next->header.prev = p->header.prev; + p->header.alloc.deallocate(); + } } -PersistentValue &PersistentValue::operator=(Object *object) +static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) { - QV4::ExecutionEngine *engine = object->engine(); - if (!d) - d = new PersistentValuePrivate(object->asReturnedValue(), engine); - else - d = d->detach(object->asReturnedValue()); - return *this; + while (engine->jsStackTop > markBase) { + Heap::Base *h = engine->popForGC(); + Q_ASSERT (h->gcGetInternalClass()->vtable->markObjects); + h->gcGetInternalClass()->vtable->markObjects(h, engine); + } } -void PersistentValue::set(ExecutionEngine *engine, const ValueRef val) +void PersistentValueStorage::mark(ExecutionEngine *e) { - if (!d) - d = new PersistentValuePrivate(val.asReturnedValue(), engine); - else - d = d->detach(val.asReturnedValue()); + Value *markBase = e->jsStackTop; + + Page *p = static_cast(firstPage); + while (p) { + for (int i = 0; i < NEntries; ++i) { + if (Managed *m = p->values[i].asManaged()) + m->mark(e); + } + drainMarkStack(e, markBase); + + p = p->header.next; + } } -void PersistentValue::set(ExecutionEngine *engine, ReturnedValue val) +ExecutionEngine *PersistentValueStorage::getEngine(Value *v) { - if (!d) - d = new PersistentValuePrivate(val, engine); - else - d = d->detach(val); + return getPage(v)->header.engine; } -void PersistentValue::set(ExecutionEngine *engine, Heap::Base *obj) + +PersistentValue::PersistentValue(const PersistentValue &other) + : val(0) { - if (!d) - d = new PersistentValuePrivate(obj->asReturnedValue(), engine); - else - d = d->detach(obj->asReturnedValue()); + if (other.val) { + val = other.engine()->memoryManager->m_persistentValues->allocate(); + *val = *other.val; + } } -WeakValue::WeakValue(const WeakValue &other) - : d(other.d) +PersistentValue::PersistentValue(ExecutionEngine *engine, const ValueRef value) { - if (d) - d->ref(); + val = engine->memoryManager->m_persistentValues->allocate(); + *val = value; } -WeakValue &WeakValue::operator=(const WeakValue &other) +PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue value) { - if (d == other.d) - return *this; + val = engine->memoryManager->m_persistentValues->allocate(); + *val = value; +} + +PersistentValue::~PersistentValue() +{ + PersistentValueStorage::free(val); +} - // the memory manager cleans up those with a refcount of 0 +PersistentValue &PersistentValue::operator=(const PersistentValue &other) +{ + if (!val) { + if (!other.val) + return *this; + val = other.engine()->memoryManager->m_persistentValues->allocate(); + } - if (d) - d->deref(); - d = other.d; - if (d) - d->ref(); + Q_ASSERT(engine() == other.engine()); + *val = *other.val; return *this; } -WeakValue::~WeakValue() +PersistentValue &PersistentValue::operator=(const WeakValue &other) { - if (d) - d->deref(); + if (!val) { + if (!other.valueRef()) + return *this; + val = other.engine()->memoryManager->m_persistentValues->allocate(); + } + + Q_ASSERT(engine() == other.engine()); + + *val = *other.valueRef(); + return *this; } -void WeakValue::set(ExecutionEngine *e, const ValueRef val) +PersistentValue &PersistentValue::operator=(Object *object) { - if (!d) - d = new PersistentValuePrivate(val.asReturnedValue(), e, /*weak*/true); - else - d = d->detach(val.asReturnedValue(), /*weak*/true); + if (!object) { + PersistentValueStorage::free(val); + return *this; + } + if (!val) + val = object->engine()->memoryManager->m_persistentValues->allocate(); + + *val = object; + return *this; } -void WeakValue::set(ExecutionEngine *e, ReturnedValue val) +void PersistentValue::set(ExecutionEngine *engine, const ValueRef value) { - if (!d) - d = new PersistentValuePrivate(val, e, /*weak*/true); - else - d = d->detach(val, /*weak*/true); + if (!val) + val = engine->memoryManager->m_persistentValues->allocate(); + *val = value; } -void WeakValue::set(ExecutionEngine *e, Heap::Base *obj) +void PersistentValue::set(ExecutionEngine *engine, ReturnedValue value) { - if (!d) - d = new PersistentValuePrivate(obj->asReturnedValue(), e, /*weak*/true); - else - d = d->detach(obj->asReturnedValue(), /*weak*/true); + if (!val) + val = engine->memoryManager->m_persistentValues->allocate(); + *val = value; } -void WeakValue::markOnce(ExecutionEngine *e) +void PersistentValue::set(ExecutionEngine *engine, Heap::Base *obj) { - if (!d) - return; - d->value.mark(e); + if (!val) + val = engine->memoryManager->m_persistentValues->allocate(); + *val = obj; } -PersistentValuePrivate::PersistentValuePrivate(ReturnedValue v, ExecutionEngine *e, bool weak) - : refcount(1) - , weak(weak) - , engine(e) - , prev(0) - , next(0) +WeakValue::WeakValue(const WeakValue &other) + : val(0) { - value.val = v; - init(); + if (other.val) { + val = other.engine()->memoryManager->m_weakValues->allocate(); + *val = *other.val; + } } -void PersistentValuePrivate::init() +WeakValue &WeakValue::operator=(const WeakValue &other) { - if (!engine) { - Managed *m = value.asManaged(); - if (!m) - return; - - engine = m->engine(); - } - if (engine && !prev) { - PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; - - prev = listRoot; - next = *listRoot; - *prev = this; - if (next) - next->prev = &this->next; + if (!val) { + if (!other.val) + return *this; + val = other.engine()->memoryManager->m_weakValues->allocate(); } + + Q_ASSERT(engine() == other.engine()); + + *val = *other.val; + return *this; } -PersistentValuePrivate::~PersistentValuePrivate() +WeakValue::~WeakValue() { + PersistentValueStorage::free(val); } -void PersistentValuePrivate::removeFromList() +void WeakValue::set(ExecutionEngine *engine, const ValueRef value) { - if (prev) { - if (next) - next->prev = prev; - *prev = next; - next = 0; - prev = 0; - } + if (!val) + val = engine->memoryManager->m_weakValues->allocate(); + *val = value; } -void PersistentValuePrivate::deref() +void WeakValue::set(ExecutionEngine *engine, ReturnedValue value) { - // if engine is not 0, they are registered with the memory manager - // and will get cleaned up in the next gc run - if (!--refcount) { - removeFromList(); - delete this; - } + if (!val) + val = engine->memoryManager->m_weakValues->allocate(); + *val = value; } -PersistentValuePrivate *PersistentValuePrivate::detach(const QV4::ReturnedValue val, bool weak) +void WeakValue::set(ExecutionEngine *engine, Heap::Base *obj) { - if (refcount == 1) { - value.val = val; - - Managed *m = value.asManaged(); - if (!prev) { - if (m) { - ExecutionEngine *engine = m->engine(); - if (engine) { - PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; - prev = listRoot; - next = *listRoot; - *prev = this; - if (next) - next->prev = &this->next; - } - } - } else if (!m) - removeFromList(); - - return this; - } - --refcount; - return new PersistentValuePrivate(val, engine, weak); + if (!val) + val = engine->memoryManager->m_weakValues->allocate(); + *val = obj; +} + +void WeakValue::markOnce(ExecutionEngine *e) +{ + if (!val) + return; + val->mark(e); } diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index 41c482fbb7..b71f943f80 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -39,114 +39,126 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct Q_QML_PRIVATE_EXPORT PersistentValuePrivate +struct Q_QML_EXPORT PersistentValueStorage { - PersistentValuePrivate(ReturnedValue v, ExecutionEngine *engine = 0, bool weak = false); - virtual ~PersistentValuePrivate(); - Value value; - uint refcount; - bool weak; - QV4::ExecutionEngine *engine; - PersistentValuePrivate **prev; - PersistentValuePrivate *next; - - void init(); - void removeFromList(); - void ref() { ++refcount; } - void deref(); - PersistentValuePrivate *detach(const ReturnedValue value, bool weak = false); - - bool checkEngine(QV4::ExecutionEngine *otherEngine) { - if (!engine) { - Q_ASSERT(!value.isObject()); - engine = otherEngine; + PersistentValueStorage(ExecutionEngine *engine); + ~PersistentValueStorage(); + + Value *allocate(); + static void free(Value *e); + + void mark(ExecutionEngine *e); + + struct Iterator { + Q_DECL_CONSTEXPR Iterator(void *p, int idx) + : p(p), index(idx) {} + void *p; + int index; + Iterator &operator++(); + bool operator !=(const Iterator &other) { + return p != other.p || index != other.index; } - return (engine == otherEngine); - } + Value &operator *(); + }; + Iterator begin() { return Iterator(firstPage, 0); } + Iterator end() { return Iterator(0, 0); } + + static ExecutionEngine *getEngine(Value *v); + + ExecutionEngine *engine; + void *firstPage; }; class Q_QML_EXPORT PersistentValue { public: - PersistentValue() : d(0) {} + PersistentValue() : val(0) {} PersistentValue(const PersistentValue &other); PersistentValue &operator=(const PersistentValue &other); PersistentValue &operator=(const WeakValue &other); PersistentValue &operator=(Object *object); ~PersistentValue(); - PersistentValue(ExecutionEngine *engine, const ValueRef val); - PersistentValue(ExecutionEngine *engine, ReturnedValue val); + PersistentValue(ExecutionEngine *engine, const ValueRef value); + PersistentValue(ExecutionEngine *engine, ReturnedValue value); - void set(ExecutionEngine *engine, const ValueRef val); - void set(ExecutionEngine *engine, ReturnedValue val); + void set(ExecutionEngine *engine, const ValueRef value); + void set(ExecutionEngine *engine, ReturnedValue value); void set(ExecutionEngine *engine, Heap::Base *obj); ReturnedValue value() const { - return (d ? d->value.asReturnedValue() : Primitive::undefinedValue().asReturnedValue()); + return (val ? val->asReturnedValue() : Encode::undefined()); + } + Value *valueRef() const { + return val; } Managed *asManaged() const { - if (!d) + if (!val) return 0; - return d->value.asManaged(); + return val->asManaged(); } ExecutionEngine *engine() const { - if (!d) + if (!val) return 0; - if (d->engine) - return d->engine; - Managed *m = d->value.asManaged(); - return m ? m->engine() : 0; + return PersistentValueStorage::getEngine(val); } - bool isUndefined() const { return !d || d->value.isUndefined(); } - bool isNullOrUndefined() const { return !d || d->value.isNullOrUndefined(); } + bool isUndefined() const { return !val || val->isUndefined(); } + bool isNullOrUndefined() const { return !val || val->isNullOrUndefined(); } void clear() { - *this = PersistentValue(); + PersistentValueStorage::free(val); + val = 0; } + bool isEmpty() { return !val; } private: friend struct ValueRef; - PersistentValuePrivate *d; + + Value *val; }; class Q_QML_EXPORT WeakValue { public: - WeakValue() : d(0) {} + WeakValue() : val(0) {} WeakValue(const WeakValue &other); WeakValue &operator=(const WeakValue &other); ~WeakValue(); - void set(ExecutionEngine *e, const ValueRef val); - void set(ExecutionEngine *e, ReturnedValue val); - void set(ExecutionEngine *e, Heap::Base *obj); + void set(ExecutionEngine *engine, const ValueRef value); + void set(ExecutionEngine *engine, ReturnedValue value); + void set(ExecutionEngine *engine, Heap::Base *obj); ReturnedValue value() const { - return (d ? d->value.asReturnedValue() : Primitive::undefinedValue().asReturnedValue()); + return (val ? val->asReturnedValue() : Encode::undefined()); + } + Value *valueRef() const { + return val; + } + Managed *asManaged() const { + if (!val) + return 0; + return val->asManaged(); } ExecutionEngine *engine() const { - if (!d) + if (!val) return 0; - if (d->engine) - return d->engine; - Managed *m = d->value.asManaged(); - return m ? m->engine() : 0; + return PersistentValueStorage::getEngine(val); } - bool isUndefined() const { return !d || d->value.isUndefined(); } - bool isNullOrUndefined() const { return !d || d->value.isNullOrUndefined(); } + bool isUndefined() const { return !val || val->isUndefined(); } + bool isNullOrUndefined() const { return !val || val->isNullOrUndefined(); } void clear() { - *this = WeakValue(); + PersistentValueStorage::free(val); + val = 0; } void markOnce(ExecutionEngine *e); private: - friend struct ValueRef; - PersistentValuePrivate *d; + Value *val; }; } // namespace QV4 diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index e12b91340d..705e5301dc 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -478,11 +478,7 @@ inline ValueRef::ValueRef(const Scoped &v) {} inline ValueRef::ValueRef(const PersistentValue &v) - : ptr(&v.d->value) -{} - -inline ValueRef::ValueRef(PersistentValuePrivate *p) - : ptr(&p->value) + : ptr(v.val) {} inline ValueRef &ValueRef::operator=(const ScopedValue &o) diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 8fff22ed86..0a0da27f41 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -534,7 +534,6 @@ struct ValueRef { template ValueRef(const Scoped &v); ValueRef(const PersistentValue &v); - ValueRef(PersistentValuePrivate *p); ValueRef(Value &v) { ptr = &v; } // Important: Do NOT add a copy constructor to this class // adding a copy constructor actually changes the calling convention, ie. diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 44bb30bd37..1cb98fd996 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -255,7 +255,7 @@ void QmlTypeWrapper::put(Managed *m, String *name, const ValueRef value) if (qobjectSingleton) { QV4::QObjectWrapper::setQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); } else if (!siinfo->scriptApi(e).isUndefined()) { - QV4::ScopedObject apiprivate(scope, QJSValuePrivate::get(siinfo->scriptApi(e))->value); + QV4::ScopedObject apiprivate(scope, QJSValuePrivate::get(siinfo->scriptApi(e))->getValue(v4)); if (!apiprivate) { QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); v4->throwError(error); diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index d0c8c08fce..ce5d3c8370 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -106,12 +106,15 @@ static void restoreJSValue(QDataStream &stream, void *data) quint32 isNullOrUndefined; stream >> isNullOrUndefined; + + // ### Optimize for the common case where we have an valid persistent + d->persistent.clear(); + if (isNullOrUndefined & 0x1) { - d->value = QV4::Primitive::nullValue().asReturnedValue(); + d->unboundData = QVariant(QMetaType::VoidStar, (void *)0); } else if (isNullOrUndefined & 0x2) { - d->value = QV4::Primitive::undefinedValue().asReturnedValue(); + d->unboundData = QVariant(); } else { - d->value = QV4::Primitive::emptyValue().asReturnedValue(); d->unboundData.load(stream); } } -- cgit v1.2.3