aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@theqtcompany.com>2015-01-12 21:55:51 +0100
committerSimon Hausmann <simon.hausmann@digia.com>2015-01-16 21:12:49 +0100
commit8ffb79bbd214c239e414dc4e9cf4569b3219bdab (patch)
treec9cc7e596be2616f8c1211f4fb7623c9153cd7d6
parent9fe1588915b935298917a0c29593eeed70da682f (diff)
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 <simon.hausmann@digia.com>
-rw-r--r--src/qml/jsapi/qjsengine.cpp105
-rw-r--r--src/qml/jsapi/qjsvalue.cpp341
-rw-r--r--src/qml/jsapi/qjsvalue_p.h52
-rw-r--r--src/qml/jsapi/qjsvalueiterator.cpp19
-rw-r--r--src/qml/jsruntime/qv4engine.cpp6
-rw-r--r--src/qml/jsruntime/qv4global_p.h2
-rw-r--r--src/qml/jsruntime/qv4mm.cpp70
-rw-r--r--src/qml/jsruntime/qv4mm_p.h10
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp386
-rw-r--r--src/qml/jsruntime/qv4persistent_p.h120
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h6
-rw-r--r--src/qml/jsruntime/qv4value_p.h1
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp2
-rw-r--r--src/qml/qml/v8/qv8engine.cpp9
14 files changed, 659 insertions, 470 deletions
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<bool*>(ptr) = vp->value.toBoolean();
- return true;
- case QMetaType::Int:
- *reinterpret_cast<int*>(ptr) = vp->value.toInt32();
- return true;
- case QMetaType::UInt:
- *reinterpret_cast<uint*>(ptr) = vp->value.toUInt32();
- return true;
- case QMetaType::LongLong:
- *reinterpret_cast<qlonglong*>(ptr) = vp->value.toInteger();
- return true;
- case QMetaType::ULongLong:
- *reinterpret_cast<qulonglong*>(ptr) = vp->value.toInteger();
- return true;
- case QMetaType::Double:
- *reinterpret_cast<double*>(ptr) = vp->value.toNumber();
- return true;
- case QMetaType::QString:
- *reinterpret_cast<QString*>(ptr) = value.toString();
- return true;
- case QMetaType::Float:
- *reinterpret_cast<float*>(ptr) = vp->value.toNumber();
- return true;
- case QMetaType::Short:
- *reinterpret_cast<short*>(ptr) = vp->value.toInt32();
- return true;
- case QMetaType::UShort:
- *reinterpret_cast<unsigned short*>(ptr) = vp->value.toUInt16();
- return true;
- case QMetaType::Char:
- *reinterpret_cast<char*>(ptr) = vp->value.toInt32();
- return true;
- case QMetaType::UChar:
- *reinterpret_cast<unsigned char*>(ptr) = vp->value.toUInt16();
- return true;
- case QMetaType::QChar:
- *reinterpret_cast<QChar*>(ptr) = vp->value.toUInt16();
- return true;
- default:
- return false;
- }
+ }
+
+ Q_ASSERT(val);
+
+ switch (type) {
+ case QMetaType::Bool:
+ *reinterpret_cast<bool*>(ptr) = val->toBoolean();
+ return true;
+ case QMetaType::Int:
+ *reinterpret_cast<int*>(ptr) = val->toInt32();
+ return true;
+ case QMetaType::UInt:
+ *reinterpret_cast<uint*>(ptr) = val->toUInt32();
+ return true;
+ case QMetaType::LongLong:
+ *reinterpret_cast<qlonglong*>(ptr) = val->toInteger();
+ return true;
+ case QMetaType::ULongLong:
+ *reinterpret_cast<qulonglong*>(ptr) = val->toInteger();
+ return true;
+ case QMetaType::Double:
+ *reinterpret_cast<double*>(ptr) = val->toNumber();
+ return true;
+ case QMetaType::QString:
+ *reinterpret_cast<QString*>(ptr) = val->toQStringNoThrow();
+ return true;
+ case QMetaType::Float:
+ *reinterpret_cast<float*>(ptr) = val->toNumber();
+ return true;
+ case QMetaType::Short:
+ *reinterpret_cast<short*>(ptr) = val->toInt32();
+ return true;
+ case QMetaType::UShort:
+ *reinterpret_cast<unsigned short*>(ptr) = val->toUInt16();
+ return true;
+ case QMetaType::Char:
+ *reinterpret_cast<char*>(ptr) = val->toInt32();
+ return true;
+ case QMetaType::UChar:
+ *reinterpret_cast<unsigned char*>(ptr) = val->toUInt16();
+ return true;
+ case QMetaType::QChar:
+ *reinterpret_cast<QChar*>(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<QV4::VariantObject>() : 0;
+ if (d->persistent.isEmpty())
+ return false;
+ return d->persistent.valueRef()->as<QV4::VariantObject>();
}
/*!
@@ -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<double>())
@@ -394,9 +447,10 @@ double QJSValue::toNumber() const
return std::numeric_limits<double>::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<QV4::QObjectWrapper> wrapper(scope, d->value);
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::QObjectWrapper> 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<RegExpObject>();
+ return !d->persistent.isEmpty() && d->persistent.valueRef()->as<RegExpObject>();
}
/*!
@@ -1143,7 +1230,7 @@ bool QJSValue::isRegExp() const
*/
bool QJSValue::isQObject() const
{
- return d->value.as<QV4::QObjectWrapper>() != 0;
+ return !d->persistent.isEmpty() && d->persistent.valueRef()->as<QV4::QObjectWrapper>() != 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 <private/qv4engine_p.h>
#include <private/qv4object_p.h>
#include <private/qflagpointer_p.h>
+#include <private/qv4mm_p.h>
+#include <private/qv4persistent_p.h>
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<QV4::ForEachIteratorObject> 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<ArrayObject> ScopedArrayObject;
typedef Scoped<FunctionObject> ScopedFunctionObject;
typedef Scoped<ExecutionContext> 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<QObjectWrapper*>(weak->value.managed());
+ QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*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<Data> 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<Page *>(reinterpret_cast<quintptr>(val) & ~((quintptr)(PageSize - 1)));
+}
+
+
+Page *allocatePage(PersistentValueStorage *storage)
{
+ PageAllocation page = WTF::PageAllocation::allocate(PageSize);
+ Page *p = reinterpret_cast<Page *>(page.base());
+
+ Q_ASSERT(!((quintptr)p & (PageSize - 1)));
+
+ p->header.engine = storage->engine;
+ p->header.alloc = page;
+ p->header.next = reinterpret_cast<Page *>(storage->firstPage);
+ p->header.prev = reinterpret_cast<Page **>(&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<Page *>(p)->values[index].tag != QV4::Value::Empty_Type)
+ return *this;
+ }
+ index = -1;
+ p = static_cast<Page *>(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<Page *>(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<Page *>(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<Page *>(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<Page *>(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<T> &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 <typename T>
ValueRef(const Scoped<T> &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);
}
}