/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtDeclarative module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
// #define COMPILEDBINDINGS_DEBUG | |
// #define REGISTER_CLEANUP_DEBUG | |
#include "private/qdeclarativecompiledbindings_p.h" | |
#include <QtDeclarative/qdeclarativeinfo.h> | |
#include <private/qdeclarativecontext_p.h> | |
#include <private/qdeclarativejsast_p.h> | |
#include <private/qdeclarativejsengine_p.h> | |
#include <private/qdeclarativeexpression_p.h> | |
#include <QtCore/qcoreapplication.h> | |
#include <QtCore/qdebug.h> | |
#include <QtCore/qnumeric.h> | |
#include <private/qdeclarativeanchors_p_p.h> | |
#include <private/qdeclarativeglobal_p.h> | |
#include <private/qdeclarativefastproperties_p.h> | |
#include <private/qdeclarativedebugtrace_p.h> | |
QT_BEGIN_NAMESPACE | |
DEFINE_BOOL_CONFIG_OPTION(qmlExperimental, QML_EXPERIMENTAL); | |
DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER); | |
DEFINE_BOOL_CONFIG_OPTION(qmlDisableFastProperties, QML_DISABLE_FAST_PROPERTIES); | |
DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP); | |
Q_GLOBAL_STATIC(QDeclarativeFastProperties, fastProperties); | |
#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) | |
# define QML_THREADED_INTERPRETER | |
#endif | |
#define FOR_EACH_QML_INSTR(F) \ | |
F(Noop) /* Nop */ \ | |
F(BindingId) /* id */ \ | |
F(Subscribe) /* subscribe */ \ | |
F(SubscribeId) /* subscribe */ \ | |
F(FetchAndSubscribe) /* fetchAndSubscribe */ \ | |
F(LoadId) /* load */ \ | |
F(LoadScope) /* load */ \ | |
F(LoadRoot) /* load */ \ | |
F(LoadAttached) /* attached */ \ | |
F(ConvertIntToReal) /* unaryop */ \ | |
F(ConvertRealToInt) /* unaryop */ \ | |
F(Real) /* real_value */ \ | |
F(Int) /* int_value */ \ | |
F(Bool) /* bool_value */ \ | |
F(String) /* string_value */ \ | |
F(AddReal) /* binaryop */ \ | |
F(AddInt) /* binaryop */ \ | |
F(AddString) /* binaryop */ \ | |
F(MinusReal) /* binaryop */ \ | |
F(MinusInt) /* binaryop */ \ | |
F(CompareReal) /* binaryop */ \ | |
F(CompareString) /* binaryop */ \ | |
F(NotCompareReal) /* binaryop */ \ | |
F(NotCompareString) /* binaryop */ \ | |
F(GreaterThanReal) /* binaryop */ \ | |
F(MaxReal) /* binaryop */ \ | |
F(MinReal) /* binaryop */ \ | |
F(NewString) /* construct */ \ | |
F(NewUrl) /* construct */ \ | |
F(CleanupUrl) /* cleanup */ \ | |
F(CleanupString) /* cleanup */ \ | |
F(Copy) /* copy */ \ | |
F(Fetch) /* fetch */ \ | |
F(Store) /* store */ \ | |
F(Skip) /* skip */ \ | |
F(Done) /* done */ \ | |
/* Speculative property resolution */ \ | |
F(InitString) /* initstring */ \ | |
F(FindGeneric) /* find */ \ | |
F(FindGenericTerminal) /* find */ \ | |
F(FindProperty) /* find */ \ | |
F(FindPropertyTerminal) /* find */ \ | |
F(CleanupGeneric) /* cleanup */ \ | |
F(ConvertGenericToReal) /* unaryop */ \ | |
F(ConvertGenericToBool) /* unaryop */ \ | |
F(ConvertGenericToString) /* unaryop */ \ | |
F(ConvertGenericToUrl) /* unaryop */ | |
#define QML_INSTR_ENUM(I) I, | |
#define QML_INSTR_ADDR(I) &&op_##I, | |
#ifdef QML_THREADED_INTERPRETER | |
# define QML_BEGIN_INSTR(I) op_##I: | |
# define QML_END_INSTR(I) ++instr; goto *instr->common.code; | |
# define QML_INSTR_HEADER void *code; | |
#else | |
# define QML_BEGIN_INSTR(I) case Instr::I: | |
# define QML_END_INSTR(I) break; | |
# define QML_INSTR_HEADER | |
#endif | |
using namespace QDeclarativeJS; | |
namespace { | |
// Supported types: int, qreal, QString (needs constr/destr), QObject*, bool | |
struct Register { | |
void setUndefined() { type = 0; } | |
void setUnknownButDefined() { type = -1; } | |
void setNaN() { setqreal(qSNaN()); } | |
bool isUndefined() const { return type == 0; } | |
void setQObject(QObject *o) { *((QObject **)data) = o; type = QMetaType::QObjectStar; } | |
QObject *getQObject() const { return *((QObject **)data); } | |
void setqreal(qreal v) { *((qreal *)data) = v; type = QMetaType::QReal; } | |
qreal getqreal() const { return *((qreal *)data); } | |
void setint(int v) { *((int *)data) = v; type = QMetaType::Int; } | |
int getint() const { return *((int *)data); } | |
void setbool(bool v) { *((bool *)data) = v; type = QMetaType::Bool; } | |
bool getbool() const { return *((bool *)data); } | |
QVariant *getvariantptr() { return (QVariant *)typeDataPtr(); } | |
QString *getstringptr() { return (QString *)typeDataPtr(); } | |
QUrl *geturlptr() { return (QUrl *)typeDataPtr(); } | |
const QVariant *getvariantptr() const { return (QVariant *)typeDataPtr(); } | |
const QString *getstringptr() const { return (QString *)typeDataPtr(); } | |
const QUrl *geturlptr() const { return (QUrl *)typeDataPtr(); } | |
void *typeDataPtr() { return (void *)&data; } | |
void *typeMemory() { return (void *)data; } | |
const void *typeDataPtr() const { return (void *)&data; } | |
const void *typeMemory() const { return (void *)data; } | |
int gettype() const { return type; } | |
void settype(int t) { type = t; } | |
int type; // Optional type | |
void *data[2]; // Object stored here | |
#ifdef REGISTER_CLEANUP_DEBUG | |
Register() { | |
type = 0; | |
} | |
~Register() { | |
int allowedTypes[] = { QMetaType::QObjectStar, QMetaType::QReal, QMetaType::Int, QMetaType::Bool, 0 }; | |
bool found = (type == 0); | |
int *ctype = allowedTypes; | |
while (!found && *ctype) { | |
found = (*ctype == type); | |
++ctype; | |
} | |
if (!found) | |
qWarning("Register leaked of type %d", type); | |
} | |
#endif | |
}; | |
} | |
class QDeclarativeCompiledBindingsPrivate : public QObjectPrivate | |
{ | |
Q_DECLARE_PUBLIC(QDeclarativeCompiledBindings) | |
public: | |
QDeclarativeCompiledBindingsPrivate(); | |
virtual ~QDeclarativeCompiledBindingsPrivate(); | |
struct Binding : public QDeclarativeAbstractBinding, public QDeclarativeDelayedError { | |
Binding() : enabled(false), updating(0), property(0), | |
scope(0), target(0), parent(0) {} | |
// Inherited from QDeclarativeAbstractBinding | |
virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags flags); | |
virtual void update(QDeclarativePropertyPrivate::WriteFlags flags); | |
virtual void destroy(); | |
int index:30; | |
bool enabled:1; | |
bool updating:1; | |
int property; | |
QObject *scope; | |
QObject *target; | |
QDeclarativeCompiledBindingsPrivate *parent; | |
}; | |
typedef QDeclarativeNotifierEndpoint Subscription; | |
Subscription *subscriptions; | |
QScriptDeclarativeClass::PersistentIdentifier *identifiers; | |
void run(Binding *, QDeclarativePropertyPrivate::WriteFlags flags); | |
const char *programData; | |
Binding *m_bindings; | |
quint32 *m_signalTable; | |
static int methodCount; | |
void init(); | |
void run(int instr, QDeclarativeContextData *context, | |
QDeclarativeDelayedError *error, QObject *scope, QObject *output, QDeclarativePropertyPrivate::WriteFlags storeFlags); | |
inline void unsubscribe(int subIndex); | |
inline void subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex); | |
inline void subscribe(QObject *o, int notifyIndex, int subIndex); | |
QDeclarativePropertyCache::Data *findproperty(QObject *obj, | |
const QScriptDeclarativeClass::Identifier &name, | |
QDeclarativeEnginePrivate *enginePriv, | |
QDeclarativePropertyCache::Data &local); | |
bool findproperty(QObject *obj, | |
Register *output, | |
QDeclarativeEnginePrivate *enginePriv, | |
int subIdx, | |
const QScriptDeclarativeClass::Identifier &name, | |
bool isTerminal); | |
void findgeneric(Register *output, // value output | |
int subIdx, // Subscription index in config | |
QDeclarativeContextData *context, // Context to search in | |
const QScriptDeclarativeClass::Identifier &name, | |
bool isTerminal); | |
}; | |
QDeclarativeCompiledBindingsPrivate::QDeclarativeCompiledBindingsPrivate() | |
: subscriptions(0), identifiers(0) | |
{ | |
} | |
QDeclarativeCompiledBindingsPrivate::~QDeclarativeCompiledBindingsPrivate() | |
{ | |
delete [] subscriptions; subscriptions = 0; | |
delete [] identifiers; identifiers = 0; | |
} | |
int QDeclarativeCompiledBindingsPrivate::methodCount = -1; | |
QDeclarativeCompiledBindings::QDeclarativeCompiledBindings(const char *program, QDeclarativeContextData *context) | |
: QObject(*(new QDeclarativeCompiledBindingsPrivate)) | |
{ | |
Q_D(QDeclarativeCompiledBindings); | |
if (d->methodCount == -1) | |
d->methodCount = QDeclarativeCompiledBindings::staticMetaObject.methodCount(); | |
d->programData = program; | |
d->init(); | |
QDeclarativeAbstractExpression::setContext(context); | |
} | |
QDeclarativeCompiledBindings::~QDeclarativeCompiledBindings() | |
{ | |
Q_D(QDeclarativeCompiledBindings); | |
delete [] d->m_bindings; | |
} | |
QDeclarativeAbstractBinding *QDeclarativeCompiledBindings::configBinding(int index, QObject *target, | |
QObject *scope, int property) | |
{ | |
Q_D(QDeclarativeCompiledBindings); | |
QDeclarativeCompiledBindingsPrivate::Binding *rv = d->m_bindings + index; | |
rv->index = index; | |
rv->property = property; | |
rv->target = target; | |
rv->scope = scope; | |
rv->parent = d; | |
addref(); // This is decremented in Binding::destroy() | |
return rv; | |
} | |
void QDeclarativeCompiledBindingsPrivate::Binding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags) | |
{ | |
if (enabled != e) { | |
enabled = e; | |
if (e) update(flags); | |
} | |
} | |
void QDeclarativeCompiledBindingsPrivate::Binding::update(QDeclarativePropertyPrivate::WriteFlags flags) | |
{ | |
QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Binding); | |
parent->run(this, flags); | |
QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Binding); | |
} | |
void QDeclarativeCompiledBindingsPrivate::Binding::destroy() | |
{ | |
enabled = false; | |
removeFromObject(); | |
clear(); | |
parent->q_func()->release(); | |
} | |
int QDeclarativeCompiledBindings::qt_metacall(QMetaObject::Call c, int id, void **) | |
{ | |
Q_D(QDeclarativeCompiledBindings); | |
if (c == QMetaObject::InvokeMetaMethod && id >= d->methodCount) { | |
id -= d->methodCount; | |
quint32 *reeval = d->m_signalTable + d->m_signalTable[id]; | |
quint32 count = *reeval; | |
++reeval; | |
for (quint32 ii = 0; ii < count; ++ii) { | |
d->run(d->m_bindings + reeval[ii], QDeclarativePropertyPrivate::DontRemoveBinding); | |
} | |
} | |
return -1; | |
} | |
void QDeclarativeCompiledBindingsPrivate::run(Binding *binding, QDeclarativePropertyPrivate::WriteFlags flags) | |
{ | |
Q_Q(QDeclarativeCompiledBindings); | |
if (!binding->enabled) | |
return; | |
QDeclarativeContextData *context = q->QDeclarativeAbstractExpression::context(); | |
if (!context || !context->isValid()) | |
return; | |
if (binding->updating) { | |
QString name; | |
if (binding->property & 0xFFFF0000) { | |
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context->engine); | |
QDeclarativeValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; | |
Q_ASSERT(vt); | |
name = QLatin1String(binding->target->metaObject()->property(binding->property & 0xFFFF).name()); | |
name.append(QLatin1String(".")); | |
name.append(QLatin1String(vt->metaObject()->property(binding->property >> 24).name())); | |
} else { | |
name = QLatin1String(binding->target->metaObject()->property(binding->property).name()); | |
} | |
qmlInfo(binding->target) << QCoreApplication::translate("QDeclarativeCompiledBindings", "Binding loop detected for property \"%1\"").arg(name); | |
return; | |
} | |
binding->updating = true; | |
if (binding->property & 0xFFFF0000) { | |
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context->engine); | |
QDeclarativeValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; | |
Q_ASSERT(vt); | |
vt->read(binding->target, binding->property & 0xFFFF); | |
QObject *target = vt; | |
run(binding->index, context, binding, binding->scope, target, flags); | |
vt->write(binding->target, binding->property & 0xFFFF, flags); | |
} else { | |
run(binding->index, context, binding, binding->scope, binding->target, flags); | |
} | |
binding->updating = false; | |
} | |
namespace { | |
// This structure is exactly 8-bytes in size | |
struct Instr { | |
enum { | |
FOR_EACH_QML_INSTR(QML_INSTR_ENUM) | |
}; | |
union { | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
quint8 packing[7]; | |
} common; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
quint8 packing; | |
quint16 column; | |
quint32 line; | |
} id; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
quint8 packing[3]; | |
quint16 subscriptions; | |
quint16 identifiers; | |
} init; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
quint16 offset; | |
quint32 index; | |
} subscribe; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
quint8 packing[2]; | |
quint32 index; | |
} load; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 output; | |
qint8 reg; | |
quint8 exceptionId; | |
quint32 id; | |
} attached; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 output; | |
qint8 reg; | |
quint8 exceptionId; | |
quint32 index; | |
} store; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 output; | |
qint8 objectReg; | |
quint8 exceptionId; | |
quint16 subscription; | |
quint16 function; | |
} fetchAndSubscribe; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 output; | |
qint8 objectReg; | |
quint8 exceptionId; | |
quint32 index; | |
} fetch; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
qint8 src; | |
quint8 packing[5]; | |
} copy; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
quint8 packing[6]; | |
} construct; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
quint8 packing[2]; | |
float value; | |
} real_value; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
quint8 packing[2]; | |
int value; | |
} int_value; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
bool value; | |
quint8 packing[5]; | |
} bool_value; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
quint16 length; | |
quint32 offset; | |
} string_value; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 output; | |
qint8 src1; | |
qint8 src2; | |
quint8 packing[4]; | |
} binaryop; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 output; | |
qint8 src; | |
quint8 packing[5]; | |
} unaryop; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
quint8 packing[2]; | |
quint32 count; | |
} skip; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
qint8 src; | |
quint8 exceptionId; | |
quint16 name; | |
quint16 subscribeIndex; | |
} find; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
qint8 reg; | |
quint8 packing[6]; | |
} cleanup; | |
struct { | |
QML_INSTR_HEADER | |
quint8 type; | |
quint8 packing[1]; | |
quint16 offset; | |
quint32 dataIdx; | |
} initstring; | |
}; | |
}; | |
struct Program { | |
quint32 bindings; | |
quint32 dataLength; | |
quint32 signalTableOffset; | |
quint32 exceptionDataOffset; | |
quint16 subscriptions; | |
quint16 identifiers; | |
quint16 instructionCount; | |
quint16 compiled; | |
const char *data() const { return ((const char *)this) + sizeof(Program); } | |
const Instr *instructions() const { return (const Instr *)(data() + dataLength); } | |
}; | |
} | |
struct QDeclarativeBindingCompilerPrivate | |
{ | |
struct Result { | |
Result() : unknownType(false), metaObject(0), type(-1), reg(-1) {} | |
bool operator==(const Result &o) const { | |
return unknownType == o.unknownType && | |
metaObject == o.metaObject && | |
type == o.type && | |
reg == o.reg; | |
} | |
bool operator!=(const Result &o) const { | |
return !(*this == o); | |
} | |
bool unknownType; | |
const QMetaObject *metaObject; | |
int type; | |
int reg; | |
QSet<QString> subscriptionSet; | |
}; | |
QDeclarativeBindingCompilerPrivate() : registers(0) {} | |
void resetInstanceState(); | |
int commitCompile(); | |
QDeclarativeParser::Object *context; | |
QDeclarativeParser::Object *component; | |
QDeclarativeParser::Property *destination; | |
QHash<QString, QDeclarativeParser::Object *> ids; | |
QDeclarativeImports imports; | |
QDeclarativeEnginePrivate *engine; | |
QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((quintptr)context, 16); } | |
bool compile(QDeclarativeJS::AST::Node *); | |
bool parseExpression(QDeclarativeJS::AST::Node *, Result &); | |
bool tryName(QDeclarativeJS::AST::Node *); | |
bool parseName(QDeclarativeJS::AST::Node *, Result &); | |
bool tryArith(QDeclarativeJS::AST::Node *); | |
bool parseArith(QDeclarativeJS::AST::Node *, Result &); | |
bool numberArith(Result &, const Result &, const Result &, QSOperator::Op op); | |
bool stringArith(Result &, const Result &, const Result &, QSOperator::Op op); | |
bool tryLogic(QDeclarativeJS::AST::Node *); | |
bool parseLogic(QDeclarativeJS::AST::Node *, Result &); | |
bool tryConditional(QDeclarativeJS::AST::Node *); | |
bool parseConditional(QDeclarativeJS::AST::Node *, Result &); | |
bool tryConstant(QDeclarativeJS::AST::Node *); | |
bool parseConstant(QDeclarativeJS::AST::Node *, Result &); | |
bool tryMethod(QDeclarativeJS::AST::Node *); | |
bool parseMethod(QDeclarativeJS::AST::Node *, Result &); | |
bool buildName(QStringList &, QDeclarativeJS::AST::Node *, QList<QDeclarativeJS::AST::ExpressionNode *> *nodes = 0); | |
bool fetch(Result &type, const QMetaObject *, int reg, int idx, const QStringList &, QDeclarativeJS::AST::ExpressionNode *); | |
quint32 registers; | |
QHash<int, QPair<int, int> > registerCleanups; | |
int acquireReg(int cleanup = Instr::Noop, int cleanupType = 0); | |
void registerCleanup(int reg, int cleanup, int cleanupType = 0); | |
void releaseReg(int); | |
int registerLiteralString(const QString &); | |
int registerString(const QString &); | |
QHash<QString, QPair<int, int> > registeredStrings; | |
QByteArray data; | |
bool subscription(const QStringList &, Result *); | |
int subscriptionIndex(const QStringList &); | |
bool subscriptionNeutral(const QSet<QString> &base, const QSet<QString> &lhs, const QSet<QString> &rhs); | |
quint8 exceptionId(QDeclarativeJS::AST::ExpressionNode *); | |
QVector<quint64> exceptions; | |
QSet<int> usedSubscriptionIds; | |
QSet<QString> subscriptionSet; | |
QHash<QString, int> subscriptionIds; | |
QVector<Instr> bytecode; | |
// Committed binding data | |
struct { | |
QList<int> offsets; | |
QList<QSet<int> > dependencies; | |
QVector<Instr> bytecode; | |
QByteArray data; | |
QHash<QString, int> subscriptionIds; | |
QVector<quint64> exceptions; | |
QHash<QString, QPair<int, int> > registeredStrings; | |
int count() const { return offsets.count(); } | |
} committed; | |
QByteArray buildSignalTable() const; | |
QByteArray buildExceptionData() const; | |
}; | |
void QDeclarativeCompiledBindingsPrivate::unsubscribe(int subIndex) | |
{ | |
QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); | |
sub->disconnect(); | |
} | |
void QDeclarativeCompiledBindingsPrivate::subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex) | |
{ | |
Q_Q(QDeclarativeCompiledBindings); | |
unsubscribe(subIndex); | |
if (p->idValues[idIndex]) { | |
QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); | |
sub->target = q; | |
sub->targetMethod = methodCount + subIndex; | |
sub->connect(&p->idValues[idIndex].bindings); | |
} | |
} | |
void QDeclarativeCompiledBindingsPrivate::subscribe(QObject *o, int notifyIndex, int subIndex) | |
{ | |
Q_Q(QDeclarativeCompiledBindings); | |
QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); | |
sub->target = q; | |
sub->targetMethod = methodCount + subIndex; | |
if (o) | |
sub->connect(o, notifyIndex); | |
else | |
sub->disconnect(); | |
} | |
// Conversion functions - these MUST match the QtScript expression path | |
inline static qreal toReal(Register *reg, int type, bool *ok = 0) | |
{ | |
if (ok) *ok = true; | |
if (type == QMetaType::QReal) { | |
return reg->getqreal(); | |
} else if (type == qMetaTypeId<QVariant>()) { | |
return reg->getvariantptr()->toReal(); | |
} else { | |
if (ok) *ok = false; | |
return 0; | |
} | |
} | |
inline static QString toString(Register *reg, int type, bool *ok = 0) | |
{ | |
if (ok) *ok = true; | |
if (type == QMetaType::QReal) { | |
return QString::number(reg->getqreal()); | |
} else if (type == QMetaType::Int) { | |
return QString::number(reg->getint()); | |
} else if (type == qMetaTypeId<QVariant>()) { | |
return reg->getvariantptr()->toString(); | |
} else if (type == QMetaType::QString) { | |
return *reg->getstringptr(); | |
} else { | |
if (ok) *ok = false; | |
return QString(); | |
} | |
} | |
inline static bool toBool(Register *reg, int type, bool *ok = 0) | |
{ | |
if (ok) *ok = true; | |
if (type == QMetaType::Bool) { | |
return reg->getbool(); | |
} else if (type == qMetaTypeId<QVariant>()) { | |
return reg->getvariantptr()->toBool(); | |
} else { | |
if (ok) *ok = false; | |
return false; | |
} | |
} | |
inline static QUrl toUrl(Register *reg, int type, QDeclarativeContextData *context, bool *ok = 0) | |
{ | |
if (ok) *ok = true; | |
QUrl base; | |
if (type == qMetaTypeId<QVariant>()) { | |
QVariant *var = reg->getvariantptr(); | |
int vt = var->type(); | |
if (vt == QVariant::Url) { | |
base = var->toUrl(); | |
} else if (vt == QVariant::ByteArray) { | |
base = QUrl(QString::fromUtf8(var->toByteArray())); | |
} else if (vt == QVariant::String) { | |
base = QUrl(var->toString()); | |
} else { | |
if (ok) *ok = false; | |
return QUrl(); | |
} | |
} else if (type == QMetaType::QString) { | |
base = QUrl(*reg->getstringptr()); | |
} else { | |
if (ok) *ok = false; | |
return QUrl(); | |
} | |
if (!base.isEmpty() && base.isRelative()) | |
return context->url.resolved(base); | |
else | |
return base; | |
} | |
static QObject *variantToQObject(const QVariant &value, bool *ok) | |
{ | |
if (ok) *ok = true; | |
if (value.userType() == QMetaType::QObjectStar) { | |
return qvariant_cast<QObject*>(value); | |
} else { | |
if (ok) *ok = false; | |
return 0; | |
} | |
} | |
bool QDeclarativeCompiledBindingsPrivate::findproperty(QObject *obj, Register *output, | |
QDeclarativeEnginePrivate *enginePriv, | |
int subIdx, const QScriptDeclarativeClass::Identifier &name, | |
bool isTerminal) | |
{ | |
if (!obj) { | |
output->setUndefined(); | |
return false; | |
} | |
QDeclarativePropertyCache::Data local; | |
QDeclarativePropertyCache::Data *property = | |
QDeclarativePropertyCache::property(QDeclarativeEnginePrivate::get(enginePriv), obj, name, local); | |
if (property) { | |
if (subIdx != -1) | |
subscribe(obj, property->notifyIndex, subIdx); | |
if (property->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { | |
void *args[] = { output->typeDataPtr(), 0 }; | |
QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); | |
output->settype(QMetaType::QObjectStar); | |
} else if (property->propType == qMetaTypeId<QVariant>()) { | |
QVariant v; | |
void *args[] = { &v, 0 }; | |
QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); | |
if (isTerminal) { | |
new (output->typeDataPtr()) QVariant(v); | |
output->settype(qMetaTypeId<QVariant>()); | |
} else { | |
bool ok; | |
output->setQObject(variantToQObject(v, &ok)); | |
if (!ok) | |
output->setUndefined(); | |
else | |
output->settype(QMetaType::QObjectStar); | |
} | |
} else { | |
if (!isTerminal) { | |
output->setUndefined(); | |
} else if (property->propType == QMetaType::QReal) { | |
void *args[] = { output->typeDataPtr(), 0 }; | |
QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); | |
output->settype(QMetaType::QReal); | |
} else if (property->propType == QMetaType::Int) { | |
void *args[] = { output->typeDataPtr(), 0 }; | |
QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); | |
output->settype(QMetaType::Int); | |
} else if (property->propType == QMetaType::Bool) { | |
void *args[] = { output->typeDataPtr(), 0 }; | |
QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); | |
output->settype(QMetaType::Bool); | |
} else if (property->propType == QMetaType::QString) { | |
new (output->typeDataPtr()) QString(); | |
void *args[] = { output->typeDataPtr(), 0 }; | |
QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); | |
output->settype(QMetaType::QString); | |
} else { | |
new (output->typeDataPtr()) | |
QVariant(obj->metaObject()->property(property->coreIndex).read(obj)); | |
output->settype(qMetaTypeId<QVariant>()); | |
} | |
} | |
return true; | |
} else { | |
output->setUndefined(); | |
return false; | |
} | |
} | |
void QDeclarativeCompiledBindingsPrivate::findgeneric(Register *output, | |
int subIdx, | |
QDeclarativeContextData *context, | |
const QScriptDeclarativeClass::Identifier &name, | |
bool isTerminal) | |
{ | |
QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(context->engine); | |
while (context) { | |
int contextPropertyIndex = context->propertyNames?context->propertyNames->value(name):-1; | |
if (contextPropertyIndex != -1) { | |
if (contextPropertyIndex < context->idValueCount) { | |
output->setQObject(context->idValues[contextPropertyIndex]); | |
output->settype(QMetaType::QObjectStar); | |
if (subIdx != -1) | |
subscribeId(context, contextPropertyIndex, subIdx); | |
} else { | |
QDeclarativeContextPrivate *cp = context->asQDeclarativeContextPrivate(); | |
const QVariant &value = cp->propertyValues.at(contextPropertyIndex); | |
if (isTerminal) { | |
new (output->typeDataPtr()) QVariant(value); | |
output->settype(qMetaTypeId<QVariant>()); | |
} else { | |
bool ok; | |
output->setQObject(variantToQObject(value, &ok)); | |
if (!ok) { output->setUndefined(); } | |
else { output->settype(QMetaType::QObjectStar); } | |
return; | |
} | |
if (subIdx != -1) | |
subscribe(context->asQDeclarativeContext(), contextPropertyIndex + cp->notifyIndex, subIdx); | |
} | |
return; | |
} | |
if (QObject *root = context->contextObject) { | |
if (findproperty(root, output, enginePriv, subIdx, name, isTerminal)) | |
return; | |
} | |
context = context->parent; | |
} | |
output->setUndefined(); | |
} | |
void QDeclarativeCompiledBindingsPrivate::init() | |
{ | |
Program *program = (Program *)programData; | |
if (program->subscriptions) | |
subscriptions = new QDeclarativeCompiledBindingsPrivate::Subscription[program->subscriptions]; | |
if (program->identifiers) | |
identifiers = new QScriptDeclarativeClass::PersistentIdentifier[program->identifiers]; | |
m_signalTable = (quint32 *)(program->data() + program->signalTableOffset); | |
m_bindings = new QDeclarativeCompiledBindingsPrivate::Binding[program->bindings]; | |
} | |
static void throwException(int id, QDeclarativeDelayedError *error, | |
Program *program, QDeclarativeContextData *context, | |
const QString &description = QString()) | |
{ | |
error->error.setUrl(context->url); | |
if (description.isEmpty()) | |
error->error.setDescription(QLatin1String("TypeError: Result of expression is not an object")); | |
else | |
error->error.setDescription(description); | |
if (id != 0xFF) { | |
quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id); | |
error->error.setLine((e >> 32) & 0xFFFFFFFF); | |
error->error.setColumn(e & 0xFFFFFFFF); | |
} else { | |
error->error.setLine(-1); | |
error->error.setColumn(-1); | |
} | |
if (!context->engine || !error->addError(QDeclarativeEnginePrivate::get(context->engine))) | |
QDeclarativeEnginePrivate::warning(context->engine, error->error); | |
} | |
static void dumpInstruction(const Instr *instr) | |
{ | |
switch (instr->common.type) { | |
case Instr::Noop: | |
qWarning().nospace() << "\t" << "Noop"; | |
break; | |
case Instr::BindingId: | |
qWarning().nospace() << instr->id.line << ":" << instr->id.column << ":"; | |
break; | |
case Instr::Subscribe: | |
qWarning().nospace() << "\t" << "Subscribe" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; | |
break; | |
case Instr::SubscribeId: | |
qWarning().nospace() << "\t" << "SubscribeId" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; | |
break; | |
case Instr::FetchAndSubscribe: | |
qWarning().nospace() << "\t" << "FetchAndSubscribe" << "\t" << instr->fetchAndSubscribe.output << "\t" << instr->fetchAndSubscribe.objectReg << "\t" << instr->fetchAndSubscribe.subscription; | |
break; | |
case Instr::LoadId: | |
qWarning().nospace() << "\t" << "LoadId" << "\t\t\t" << instr->load.index << "\t" << instr->load.reg; | |
break; | |
case Instr::LoadScope: | |
qWarning().nospace() << "\t" << "LoadScope" << "\t\t" << instr->load.index << "\t" << instr->load.reg; | |
break; | |
case Instr::LoadRoot: | |
qWarning().nospace() << "\t" << "LoadRoot" << "\t\t" << instr->load.index << "\t" << instr->load.reg; | |
break; | |
case Instr::LoadAttached: | |
qWarning().nospace() << "\t" << "LoadAttached" << "\t\t" << instr->attached.output << "\t" << instr->attached.reg << "\t" << instr->attached.id; | |
break; | |
case Instr::ConvertIntToReal: | |
qWarning().nospace() << "\t" << "ConvertIntToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; | |
break; | |
case Instr::ConvertRealToInt: | |
qWarning().nospace() << "\t" << "ConvertRealToInt" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; | |
break; | |
case Instr::Real: | |
qWarning().nospace() << "\t" << "Real" << "\t\t\t" << instr->real_value.reg << "\t" << instr->real_value.value; | |
break; | |
case Instr::Int: | |
qWarning().nospace() << "\t" << "Int" << "\t\t\t" << instr->int_value.reg << "\t" << instr->int_value.value; | |
break; | |
case Instr::Bool: | |
qWarning().nospace() << "\t" << "Bool" << "\t\t\t" << instr->bool_value.reg << "\t" << instr->bool_value.value; | |
break; | |
case Instr::String: | |
qWarning().nospace() << "\t" << "String" << "\t\t\t" << instr->string_value.reg << "\t" << instr->string_value.offset << "\t" << instr->string_value.length; | |
break; | |
case Instr::AddReal: | |
qWarning().nospace() << "\t" << "AddReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::AddInt: | |
qWarning().nospace() << "\t" << "AddInt" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::AddString: | |
qWarning().nospace() << "\t" << "AddString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::MinusReal: | |
qWarning().nospace() << "\t" << "MinusReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::MinusInt: | |
qWarning().nospace() << "\t" << "MinusInt" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::CompareReal: | |
qWarning().nospace() << "\t" << "CompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::CompareString: | |
qWarning().nospace() << "\t" << "CompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::NotCompareReal: | |
qWarning().nospace() << "\t" << "NotCompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::NotCompareString: | |
qWarning().nospace() << "\t" << "NotCompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::GreaterThanReal: | |
qWarning().nospace() << "\t" << "GreaterThanReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::MaxReal: | |
qWarning().nospace() << "\t" << "MaxReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::MinReal: | |
qWarning().nospace() << "\t" << "MinReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; | |
break; | |
case Instr::NewString: | |
qWarning().nospace() << "\t" << "NewString" << "\t\t" << instr->construct.reg; | |
break; | |
case Instr::NewUrl: | |
qWarning().nospace() << "\t" << "NewUrl" << "\t\t\t" << instr->construct.reg; | |
break; | |
case Instr::CleanupString: | |
qWarning().nospace() << "\t" << "CleanupString" << "\t\t" << instr->cleanup.reg; | |
break; | |
case Instr::CleanupUrl: | |
qWarning().nospace() << "\t" << "CleanupUrl" << "\t\t" << instr->cleanup.reg; | |
break; | |
case Instr::Fetch: | |
qWarning().nospace() << "\t" << "Fetch" << "\t\t\t" << instr->fetch.output << "\t" << instr->fetch.index << "\t" << instr->fetch.objectReg; | |
break; | |
case Instr::Store: | |
qWarning().nospace() << "\t" << "Store" << "\t\t\t" << instr->store.output << "\t" << instr->store.index << "\t" << instr->store.reg; | |
break; | |
case Instr::Copy: | |
qWarning().nospace() << "\t" << "Copy" << "\t\t\t" << instr->copy.reg << "\t" << instr->copy.src; | |
break; | |
case Instr::Skip: | |
qWarning().nospace() << "\t" << "Skip" << "\t\t\t" << instr->skip.reg << "\t" << instr->skip.count; | |
break; | |
case Instr::Done: | |
qWarning().nospace() << "\t" << "Done"; | |
break; | |
case Instr::InitString: | |
qWarning().nospace() << "\t" << "InitString" << "\t\t" << instr->initstring.offset << "\t" << instr->initstring.dataIdx; | |
break; | |
case Instr::FindGeneric: | |
qWarning().nospace() << "\t" << "FindGeneric" << "\t\t" << instr->find.reg << "\t" << instr->find.name; | |
break; | |
case Instr::FindGenericTerminal: | |
qWarning().nospace() << "\t" << "FindGenericTerminal" << "\t" << instr->find.reg << "\t" << instr->find.name; | |
break; | |
case Instr::FindProperty: | |
qWarning().nospace() << "\t" << "FindProperty" << "\t\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name; | |
break; | |
case Instr::FindPropertyTerminal: | |
qWarning().nospace() << "\t" << "FindPropertyTerminal" << "\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name; | |
break; | |
case Instr::CleanupGeneric: | |
qWarning().nospace() << "\t" << "CleanupGeneric" << "\t\t" << instr->cleanup.reg; | |
break; | |
case Instr::ConvertGenericToReal: | |
qWarning().nospace() << "\t" << "ConvertGenericToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; | |
break; | |
case Instr::ConvertGenericToBool: | |
qWarning().nospace() << "\t" << "ConvertGenericToBool" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; | |
break; | |
case Instr::ConvertGenericToString: | |
qWarning().nospace() << "\t" << "ConvertGenericToString" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; | |
break; | |
case Instr::ConvertGenericToUrl: | |
qWarning().nospace() << "\t" << "ConvertGenericToUrl" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; | |
break; | |
default: | |
qWarning().nospace() << "\t" << "Unknown"; | |
break; | |
} | |
} | |
void QDeclarativeCompiledBindingsPrivate::run(int instrIndex, | |
QDeclarativeContextData *context, QDeclarativeDelayedError *error, | |
QObject *scope, QObject *output, QDeclarativePropertyPrivate::WriteFlags storeFlags) | |
{ | |
Q_Q(QDeclarativeCompiledBindings); | |
error->removeError(); | |
Register registers[32]; | |
QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(context->engine); | |
Program *program = (Program *)programData; | |
const Instr *instr = program->instructions(); | |
instr += instrIndex; | |
const char *data = program->data(); | |
#ifdef QML_THREADED_INTERPRETER | |
static void *decode_instr[] = { | |
FOR_EACH_QML_INSTR(QML_INSTR_ADDR) | |
}; | |
if (!program->compiled) { | |
program->compiled = true; | |
const Instr *inop = program->instructions(); | |
for (int i = 0; i < program->instructionCount; ++i) { | |
Instr *op = (Instr *) inop++; | |
op->common.code = decode_instr[op->common.type]; | |
} | |
} | |
goto *instr->common.code; | |
#else | |
// return; | |
#ifdef COMPILEDBINDINGS_DEBUG | |
qWarning().nospace() << "Begin binding run"; | |
#endif | |
while (instr) { | |
switch (instr->common.type) { | |
#ifdef COMPILEDBINDINGS_DEBUG | |
dumpInstruction(instr); | |
#endif | |
#endif | |
QML_BEGIN_INSTR(Noop) | |
QML_END_INSTR(Noop) | |
QML_BEGIN_INSTR(BindingId) | |
QML_END_INSTR(BindingId) | |
QML_BEGIN_INSTR(SubscribeId) | |
subscribeId(context, instr->subscribe.index, instr->subscribe.offset); | |
QML_END_INSTR(SubscribeId) | |
QML_BEGIN_INSTR(Subscribe) | |
{ | |
QObject *o = 0; | |
const Register &object = registers[instr->subscribe.reg]; | |
if (!object.isUndefined()) o = object.getQObject(); | |
subscribe(o, instr->subscribe.index, instr->subscribe.offset); | |
} | |
QML_END_INSTR(Subscribe) | |
QML_BEGIN_INSTR(FetchAndSubscribe) | |
{ | |
const Register &input = registers[instr->fetchAndSubscribe.objectReg]; | |
Register &output = registers[instr->fetchAndSubscribe.output]; | |
if (input.isUndefined()) { | |
throwException(instr->fetchAndSubscribe.exceptionId, error, program, context); | |
return; | |
} | |
QObject *object = input.getQObject(); | |
if (!object) { | |
output.setUndefined(); | |
} else { | |
int subIdx = instr->fetchAndSubscribe.subscription; | |
QDeclarativeCompiledBindingsPrivate::Subscription *sub = 0; | |
if (subIdx != -1) { | |
sub = (subscriptions + subIdx); | |
sub->target = q; | |
sub->targetMethod = methodCount + subIdx; | |
} | |
fastProperties()->accessor(instr->fetchAndSubscribe.function)(object, output.typeDataPtr(), sub); | |
} | |
} | |
QML_END_INSTR(FetchAndSubscribe) | |
QML_BEGIN_INSTR(LoadId) | |
registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data()); | |
QML_END_INSTR(LoadId) | |
QML_BEGIN_INSTR(LoadScope) | |
registers[instr->load.reg].setQObject(scope); | |
QML_END_INSTR(LoadScope) | |
QML_BEGIN_INSTR(LoadRoot) | |
registers[instr->load.reg].setQObject(context->contextObject); | |
QML_END_INSTR(LoadRoot) | |
QML_BEGIN_INSTR(LoadAttached) | |
{ | |
const Register &input = registers[instr->attached.reg]; | |
Register &output = registers[instr->attached.output]; | |
if (input.isUndefined()) { | |
throwException(instr->attached.exceptionId, error, program, context); | |
return; | |
} | |
QObject *object = registers[instr->attached.reg].getQObject(); | |
if (!object) { | |
output.setUndefined(); | |
} else { | |
QObject *attached = | |
qmlAttachedPropertiesObjectById(instr->attached.id, | |
registers[instr->attached.reg].getQObject(), | |
true); | |
Q_ASSERT(attached); | |
output.setQObject(attached); | |
} | |
} | |
QML_END_INSTR(LoadAttached) | |
QML_BEGIN_INSTR(ConvertIntToReal) | |
{ | |
const Register &input = registers[instr->unaryop.src]; | |
Register &output = registers[instr->unaryop.output]; | |
if (input.isUndefined()) output.setUndefined(); | |
else output.setqreal(qreal(input.getint())); | |
} | |
QML_END_INSTR(ConvertIntToReal) | |
QML_BEGIN_INSTR(ConvertRealToInt) | |
{ | |
const Register &input = registers[instr->unaryop.src]; | |
Register &output = registers[instr->unaryop.output]; | |
if (input.isUndefined()) output.setUndefined(); | |
else output.setint(qRound(input.getqreal())); | |
} | |
QML_END_INSTR(ConvertRealToInt) | |
QML_BEGIN_INSTR(Real) | |
registers[instr->real_value.reg].setqreal(instr->real_value.value); | |
QML_END_INSTR(Real) | |
QML_BEGIN_INSTR(Int) | |
registers[instr->int_value.reg].setint(instr->int_value.value); | |
QML_END_INSTR(Int) | |
QML_BEGIN_INSTR(Bool) | |
registers[instr->bool_value.reg].setbool(instr->bool_value.value); | |
QML_END_INSTR(Bool) | |
QML_BEGIN_INSTR(String) | |
{ | |
Register &output = registers[instr->string_value.reg]; | |
new (output.getstringptr()) | |
QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); | |
output.settype(QMetaType::QString); | |
} | |
QML_END_INSTR(String) | |
QML_BEGIN_INSTR(AddReal) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); | |
else output.setqreal(lhs.getqreal() + rhs.getqreal()); | |
} | |
QML_END_INSTR(AddReal) | |
QML_BEGIN_INSTR(AddInt) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); | |
else output.setint(lhs.getint() + rhs.getint()); | |
} | |
QML_END_INSTR(AddInt) | |
QML_BEGIN_INSTR(AddString) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() && rhs.isUndefined()) { output.setNaN(); } | |
else { | |
if (lhs.isUndefined()) | |
new (output.getstringptr()) | |
QString(QLatin1String("undefined") + *registers[instr->binaryop.src2].getstringptr()); | |
else if (rhs.isUndefined()) | |
new (output.getstringptr()) | |
QString(*registers[instr->binaryop.src1].getstringptr() + QLatin1String("undefined")); | |
else | |
new (output.getstringptr()) | |
QString(*registers[instr->binaryop.src1].getstringptr() + | |
*registers[instr->binaryop.src2].getstringptr()); | |
output.settype(QMetaType::QString); | |
} | |
} | |
QML_END_INSTR(AddString) | |
QML_BEGIN_INSTR(MinusReal) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); | |
else output.setqreal(lhs.getqreal() - rhs.getqreal()); | |
} | |
QML_END_INSTR(MinusReal) | |
QML_BEGIN_INSTR(MinusInt) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); | |
else output.setint(lhs.getint() - rhs.getint()); | |
} | |
QML_END_INSTR(MinusInt) | |
QML_BEGIN_INSTR(CompareReal) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); | |
else output.setbool(lhs.getqreal() == rhs.getqreal()); | |
} | |
QML_END_INSTR(CompareReal) | |
QML_BEGIN_INSTR(CompareString) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); | |
else output.setbool(*lhs.getstringptr() == *rhs.getstringptr()); | |
} | |
QML_END_INSTR(CompareString) | |
QML_BEGIN_INSTR(NotCompareReal) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); | |
else output.setbool(lhs.getqreal() != rhs.getqreal()); | |
} | |
QML_END_INSTR(NotCompareReal) | |
QML_BEGIN_INSTR(NotCompareString) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); | |
else output.setbool(*lhs.getstringptr() != *rhs.getstringptr()); | |
} | |
QML_END_INSTR(NotCompareString) | |
QML_BEGIN_INSTR(GreaterThanReal) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(false); | |
else output.setbool(lhs.getqreal() > rhs.getqreal()); | |
} | |
QML_END_INSTR(GreaterThanReal) | |
QML_BEGIN_INSTR(MaxReal) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); | |
else output.setqreal(qMax(lhs.getqreal(), rhs.getqreal())); | |
} | |
QML_END_INSTR(MaxReal) | |
QML_BEGIN_INSTR(MinReal) | |
{ | |
const Register &lhs = registers[instr->binaryop.src1]; | |
const Register &rhs = registers[instr->binaryop.src2]; | |
Register &output = registers[instr->binaryop.output]; | |
if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); | |
else output.setqreal(qMin(lhs.getqreal(), rhs.getqreal())); | |
} | |
QML_END_INSTR(MinReal) | |
QML_BEGIN_INSTR(NewString) | |
{ | |
Register &output = registers[instr->construct.reg]; | |
new (output.getstringptr()) QString; | |
output.settype(QMetaType::QString); | |
} | |
QML_END_INSTR(NewString) | |
QML_BEGIN_INSTR(NewUrl) | |
{ | |
Register &output = registers[instr->construct.reg]; | |
new (output.geturlptr()) QUrl; | |
output.settype(QMetaType::QUrl); | |
} | |
QML_END_INSTR(NewUrl) | |
QML_BEGIN_INSTR(CleanupString) | |
registers[instr->cleanup.reg].getstringptr()->~QString(); | |
#ifdef REGISTER_CLEANUP_DEBUG | |
registers[instr->cleanup.reg].setUndefined(); | |
#endif | |
QML_END_INSTR(CleanupString) | |
QML_BEGIN_INSTR(CleanupUrl) | |
registers[instr->cleanup.reg].geturlptr()->~QUrl(); | |
#ifdef REGISTER_CLEANUP_DEBUG | |
registers[instr->cleanup.reg].setUndefined(); | |
#endif | |
QML_END_INSTR(CleanupUrl) | |
QML_BEGIN_INSTR(Fetch) | |
{ | |
const Register &input = registers[instr->fetch.objectReg]; | |
Register &output = registers[instr->fetch.output]; | |
if (input.isUndefined()) { | |
throwException(instr->fetch.exceptionId, error, program, context); | |
return; | |
} | |
QObject *object = input.getQObject(); | |
if (!object) { | |
output.setUndefined(); | |
} else { | |
void *argv[] = { output.typeDataPtr(), 0 }; | |
QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); | |
} | |
} | |
QML_END_INSTR(Fetch) | |
QML_BEGIN_INSTR(Store) | |
{ | |
Register &data = registers[instr->store.reg]; | |
if (data.isUndefined()) { | |
throwException(instr->store.exceptionId, error, program, context, | |
QLatin1String("Unable to assign undefined value")); | |
return; | |
} | |
int status = -1; | |
void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; | |
QMetaObject::metacall(output, QMetaObject::WriteProperty, | |
instr->store.index, argv); | |
} | |
QML_END_INSTR(Store) | |
QML_BEGIN_INSTR(Copy) | |
registers[instr->copy.reg] = registers[instr->copy.src]; | |
QML_END_INSTR(Copy) | |
QML_BEGIN_INSTR(Skip) | |
if (instr->skip.reg == -1 || !registers[instr->skip.reg].getbool()) | |
instr += instr->skip.count; | |
QML_END_INSTR(Skip) | |
QML_BEGIN_INSTR(Done) | |
return; | |
QML_END_INSTR(Done) | |
QML_BEGIN_INSTR(InitString) | |
if (!identifiers[instr->initstring.offset].identifier) { | |
quint32 len = *(quint32 *)(data + instr->initstring.dataIdx); | |
QChar *strdata = (QChar *)(data + instr->initstring.dataIdx + sizeof(quint32)); | |
QString str = QString::fromRawData(strdata, len); | |
identifiers[instr->initstring.offset] = engine->objectClass->createPersistentIdentifier(str); | |
} | |
QML_END_INSTR(InitString) | |
QML_BEGIN_INSTR(FindGenericTerminal) | |
// We start the search in the parent context, as we know that the | |
// name is not present in the current context or it would have been | |
// found during the static compile | |
findgeneric(registers + instr->find.reg, instr->find.subscribeIndex, | |
context->parent, | |
identifiers[instr->find.name].identifier, | |
instr->common.type == Instr::FindGenericTerminal); | |
QML_END_INSTR(FindGenericTerminal) | |
QML_BEGIN_INSTR(FindGeneric) | |
// We start the search in the parent context, as we know that the | |
// name is not present in the current context or it would have been | |
// found during the static compile | |
findgeneric(registers + instr->find.reg, instr->find.subscribeIndex, | |
context->parent, | |
identifiers[instr->find.name].identifier, | |
instr->common.type == Instr::FindGenericTerminal); | |
QML_END_INSTR(FindGeneric) | |
QML_BEGIN_INSTR(FindPropertyTerminal) | |
{ | |
const Register &object = registers[instr->find.src]; | |
if (object.isUndefined()) { | |
throwException(instr->find.exceptionId, error, program, context); | |
return; | |
} | |
findproperty(object.getQObject(), registers + instr->find.reg, | |
QDeclarativeEnginePrivate::get(context->engine), | |
instr->find.subscribeIndex, identifiers[instr->find.name].identifier, | |
instr->common.type == Instr::FindPropertyTerminal); | |
} | |
QML_END_INSTR(FindPropertyTerminal) | |
QML_BEGIN_INSTR(FindProperty) | |
{ | |
const Register &object = registers[instr->find.src]; | |
if (object.isUndefined()) { | |
throwException(instr->find.exceptionId, error, program, context); | |
return; | |
} | |
findproperty(object.getQObject(), registers + instr->find.reg, | |
QDeclarativeEnginePrivate::get(context->engine), | |
instr->find.subscribeIndex, identifiers[instr->find.name].identifier, | |
instr->common.type == Instr::FindPropertyTerminal); | |
} | |
QML_END_INSTR(FindProperty) | |
QML_BEGIN_INSTR(CleanupGeneric) | |
{ | |
int type = registers[instr->cleanup.reg].gettype(); | |
if (type == qMetaTypeId<QVariant>()) { | |
registers[instr->cleanup.reg].getvariantptr()->~QVariant(); | |
#ifdef REGISTER_CLEANUP_DEBUG | |
registers[instr->cleanup.reg].setUndefined(); | |
#endif | |
} else if (type == QMetaType::QString) { | |
registers[instr->cleanup.reg].getstringptr()->~QString(); | |
#ifdef REGISTER_CLEANUP_DEBUG | |
registers[instr->cleanup.reg].setUndefined(); | |
#endif | |
} else if (type == QMetaType::QUrl) { | |
registers[instr->cleanup.reg].geturlptr()->~QUrl(); | |
#ifdef REGISTER_CLEANUP_DEBUG | |
registers[instr->cleanup.reg].setUndefined(); | |
#endif | |
} | |
} | |
QML_END_INSTR(CleanupGeneric) | |
QML_BEGIN_INSTR(ConvertGenericToReal) | |
{ | |
Register &output = registers[instr->unaryop.output]; | |
Register &input = registers[instr->unaryop.src]; | |
bool ok = true; | |
output.setqreal(toReal(&input, input.gettype(), &ok)); | |
if (!ok) output.setUndefined(); | |
} | |
QML_END_INSTR(ConvertGenericToReal) | |
QML_BEGIN_INSTR(ConvertGenericToBool) | |
{ | |
Register &output = registers[instr->unaryop.output]; | |
Register &input = registers[instr->unaryop.src]; | |
bool ok = true; | |
output.setbool(toBool(&input, input.gettype(), &ok)); | |
if (!ok) output.setUndefined(); | |
} | |
QML_END_INSTR(ConvertGenericToBool) | |
QML_BEGIN_INSTR(ConvertGenericToString) | |
{ | |
Register &output = registers[instr->unaryop.output]; | |
Register &input = registers[instr->unaryop.src]; | |
bool ok = true; | |
QString str = toString(&input, input.gettype(), &ok); | |
if (ok) { new (output.getstringptr()) QString(str); output.settype(QMetaType::QString); } | |
else { output.setUndefined(); } | |
} | |
QML_END_INSTR(ConvertGenericToString) | |
QML_BEGIN_INSTR(ConvertGenericToUrl) | |
{ | |
Register &output = registers[instr->unaryop.output]; | |
Register &input = registers[instr->unaryop.src]; | |
bool ok = true; | |
QUrl url = toUrl(&input, input.gettype(), context, &ok); | |
if (ok) { new (output.geturlptr()) QUrl(url); output.settype(QMetaType::QUrl); } | |
else { output.setUndefined(); } | |
} | |
QML_END_INSTR(ConvertGenericToUrl) | |
#ifdef QML_THREADED_INTERPRETER | |
// nothing to do | |
#else | |
default: | |
qFatal("EEK"); | |
break; | |
} // switch | |
++instr; | |
} // while | |
#endif | |
} | |
void QDeclarativeBindingCompiler::dump(const QByteArray &programData) | |
{ | |
const Program *program = (const Program *)programData.constData(); | |
qWarning() << "Program.bindings:" << program->bindings; | |
qWarning() << "Program.dataLength:" << program->dataLength; | |
qWarning() << "Program.subscriptions:" << program->subscriptions; | |
qWarning() << "Program.indentifiers:" << program->identifiers; | |
int count = program->instructionCount; | |
const Instr *instr = program->instructions(); | |
while (count--) { | |
dumpInstruction(instr); | |
++instr; | |
} | |
} | |
/*! | |
Clear the state associated with attempting to compile a specific binding. | |
This does not clear the global "committed binding" states. | |
*/ | |
void QDeclarativeBindingCompilerPrivate::resetInstanceState() | |
{ | |
registers = 0; | |
registerCleanups.clear(); | |
data = committed.data; | |
exceptions = committed.exceptions; | |
usedSubscriptionIds.clear(); | |
subscriptionSet.clear(); | |
subscriptionIds = committed.subscriptionIds; | |
registeredStrings = committed.registeredStrings; | |
bytecode.clear(); | |
} | |
/*! | |
Mark the last compile as successful, and add it to the "committed data" | |
section. | |
Returns the index for the committed binding. | |
*/ | |
int QDeclarativeBindingCompilerPrivate::commitCompile() | |
{ | |
int rv = committed.count(); | |
committed.offsets << committed.bytecode.count(); | |
committed.dependencies << usedSubscriptionIds; | |
committed.bytecode << bytecode; | |
committed.data = data; | |
committed.exceptions = exceptions; | |
committed.subscriptionIds = subscriptionIds; | |
committed.registeredStrings = registeredStrings; | |
return rv; | |
} | |
bool QDeclarativeBindingCompilerPrivate::compile(QDeclarativeJS::AST::Node *node) | |
{ | |
resetInstanceState(); | |
if (destination->type == -1) | |
return false; | |
if (bindingsDump()) { | |
QDeclarativeJS::AST::ExpressionNode *n = node->expressionCast(); | |
if (n) { | |
Instr id; | |
id.common.type = Instr::BindingId; | |
id.id.column = n->firstSourceLocation().startColumn; | |
id.id.line = n->firstSourceLocation().startLine; | |
bytecode << id; | |
} | |
} | |
Result type; | |
if (!parseExpression(node, type)) | |
return false; | |
if (subscriptionSet.count() > 0xFFFF || | |
registeredStrings.count() > 0xFFFF) | |
return false; | |
if (type.unknownType) { | |
if (!qmlExperimental()) | |
return false; | |
if (destination->type != QMetaType::QReal && | |
destination->type != QVariant::String && | |
destination->type != QMetaType::Bool && | |
destination->type != QVariant::Url) | |
return false; | |
int convertReg = acquireReg(); | |
if (convertReg == -1) | |
return false; | |
if (destination->type == QMetaType::QReal) { | |
Instr convert; | |
convert.common.type = Instr::ConvertGenericToReal; | |
convert.unaryop.output = convertReg; | |
convert.unaryop.src = type.reg; | |
bytecode << convert; | |
} else if (destination->type == QVariant::String) { | |
Instr convert; | |
convert.common.type = Instr::ConvertGenericToString; | |
convert.unaryop.output = convertReg; | |
convert.unaryop.src = type.reg; | |
bytecode << convert; | |
} else if (destination->type == QMetaType::Bool) { | |
Instr convert; | |
convert.common.type = Instr::ConvertGenericToBool; | |
convert.unaryop.output = convertReg; | |
convert.unaryop.src = type.reg; | |
bytecode << convert; | |
} else if (destination->type == QVariant::Url) { | |
Instr convert; | |
convert.common.type = Instr::ConvertGenericToUrl; | |
convert.unaryop.output = convertReg; | |
convert.unaryop.src = type.reg; | |
bytecode << convert; | |
} | |
Instr cleanup; | |
cleanup.common.type = Instr::CleanupGeneric; | |
cleanup.cleanup.reg = type.reg; | |
bytecode << cleanup; | |
Instr instr; | |
instr.common.type = Instr::Store; | |
instr.store.output = 0; | |
instr.store.index = destination->index; | |
instr.store.reg = convertReg; | |
instr.store.exceptionId = exceptionId(node->expressionCast()); | |
bytecode << instr; | |
if (destination->type == QVariant::String) { | |
Instr cleanup; | |
cleanup.common.type = Instr::CleanupString; | |
cleanup.cleanup.reg = convertReg; | |
bytecode << cleanup; | |
} else if (destination->type == QVariant::Url) { | |
Instr cleanup; | |
cleanup.common.type = Instr::CleanupUrl; | |
cleanup.cleanup.reg = convertReg; | |
bytecode << cleanup; | |
} | |
releaseReg(convertReg); | |
Instr done; | |
done.common.type = Instr::Done; | |
bytecode << done; | |
} else { | |
// Can we store the final value? | |
if (type.type == QVariant::Int && | |
destination->type == QMetaType::QReal) { | |
Instr instr; | |
instr.common.type = Instr::ConvertIntToReal; | |
instr.unaryop.output = type.reg; | |
instr.unaryop.src = type.reg; | |
bytecode << instr; | |
type.type = QMetaType::QReal; | |
} else if (type.type == QMetaType::QReal && | |
destination->type == QVariant::Int) { | |
Instr instr; | |
instr.common.type = Instr::ConvertRealToInt; | |
instr.unaryop.output = type.reg; | |
instr.unaryop.src = type.reg; | |
bytecode << instr; | |
type.type = QVariant::Int; | |
} else if (type.type == destination->type) { | |
} else { | |
const QMetaObject *from = type.metaObject; | |
const QMetaObject *to = engine->rawMetaObjectForType(destination->type); | |
if (QDeclarativePropertyPrivate::canConvert(from, to)) | |
type.type = destination->type; | |
} | |
if (type.type == destination->type) { | |
Instr instr; | |
instr.common.type = Instr::Store; | |
instr.store.output = 0; | |
instr.store.index = destination->index; | |
instr.store.reg = type.reg; | |
instr.store.exceptionId = exceptionId(node->expressionCast()); | |
bytecode << instr; | |
releaseReg(type.reg); | |
Instr done; | |
done.common.type = Instr::Done; | |
bytecode << done; | |
} else { | |
return false; | |
} | |
} | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::parseExpression(QDeclarativeJS::AST::Node *node, Result &type) | |
{ | |
while (node->kind == AST::Node::Kind_NestedExpression) | |
node = static_cast<AST::NestedExpression *>(node)->expression; | |
if (tryArith(node)) { | |
if (!parseArith(node, type)) return false; | |
} else if (tryLogic(node)) { | |
if (!parseLogic(node, type)) return false; | |
} else if (tryConditional(node)) { | |
if (!parseConditional(node, type)) return false; | |
} else if (tryName(node)) { | |
if (!parseName(node, type)) return false; | |
} else if (tryConstant(node)) { | |
if (!parseConstant(node, type)) return false; | |
} else if (tryMethod(node)) { | |
if (!parseMethod(node, type)) return false; | |
} else { | |
return false; | |
} | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::tryName(QDeclarativeJS::AST::Node *node) | |
{ | |
return node->kind == AST::Node::Kind_IdentifierExpression || | |
node->kind == AST::Node::Kind_FieldMemberExpression; | |
} | |
bool QDeclarativeBindingCompilerPrivate::parseName(AST::Node *node, Result &type) | |
{ | |
QStringList nameParts; | |
QList<AST::ExpressionNode *> nameNodes; | |
if (!buildName(nameParts, node, &nameNodes)) | |
return false; | |
int reg = acquireReg(); | |
if (reg == -1) | |
return false; | |
type.reg = reg; | |
QDeclarativeParser::Object *absType = 0; | |
QStringList subscribeName; | |
bool wasAttachedObject = false; | |
for (int ii = 0; ii < nameParts.count(); ++ii) { | |
const QString &name = nameParts.at(ii); | |
// We don't handle signal properties or attached properties | |
if (name.length() > 2 && name.startsWith(QLatin1String("on")) && | |
name.at(2).isUpper()) | |
return false; | |
QDeclarativeType *attachType = 0; | |
if (name.at(0).isUpper()) { | |
// Could be an attached property | |
if (ii == nameParts.count() - 1) | |
return false; | |
if (nameParts.at(ii + 1).at(0).isUpper()) | |
return false; | |
QDeclarativeImportedNamespace *ns = 0; | |
if (!imports.resolveType(name.toUtf8(), &attachType, 0, 0, 0, &ns)) | |
return false; | |
if (ns || !attachType || !attachType->attachedPropertiesType()) | |
return false; | |
wasAttachedObject = true; | |
} | |
if (ii == 0) { | |
if (attachType) { | |
Instr instr; | |
instr.common.type = Instr::LoadScope; | |
instr.load.index = 0; | |
instr.load.reg = reg; | |
bytecode << instr; | |
Instr attach; | |
attach.common.type = Instr::LoadAttached; | |
attach.attached.output = reg; | |
attach.attached.reg = reg; | |
attach.attached.id = attachType->attachedPropertiesId(); | |
attach.attached.exceptionId = exceptionId(nameNodes.at(ii)); | |
bytecode << attach; | |
subscribeName << contextName(); | |
subscribeName << QLatin1String("$$$ATTACH_") + name; | |
absType = 0; | |
type.metaObject = attachType->attachedPropertiesType(); | |
continue; | |
} else if (ids.contains(name)) { | |
QDeclarativeParser::Object *idObject = ids.value(name); | |
absType = idObject; | |
type.metaObject = absType->metaObject(); | |
// We check if the id object is the root or | |
// scope object to avoid a subscription | |
if (idObject == component) { | |
Instr instr; | |
instr.common.type = Instr::LoadRoot; | |
instr.load.index = 0; | |
instr.load.reg = reg; | |
bytecode << instr; | |
} else if (idObject == context) { | |
Instr instr; | |
instr.common.type = Instr::LoadScope; | |
instr.load.index = 0; | |
instr.load.reg = reg; | |
bytecode << instr; | |
} else { | |
Instr instr; | |
instr.common.type = Instr::LoadId; | |
instr.load.index = idObject->idIndex; | |
instr.load.reg = reg; | |
bytecode << instr; | |
subscribeName << QLatin1String("$$$ID_") + name; | |
if (subscription(subscribeName, &type)) { | |
Instr sub; | |
sub.common.type = Instr::SubscribeId; | |
sub.subscribe.offset = subscriptionIndex(subscribeName); | |
sub.subscribe.reg = reg; | |
sub.subscribe.index = instr.load.index; | |
bytecode << sub; | |
} | |
} | |
} else { | |
QByteArray utf8Name = name.toUtf8(); | |
const char *cname = utf8Name.constData(); | |
int d0Idx = (context == component)?-1:context->metaObject()->indexOfProperty(cname); | |
int d1Idx = -1; | |
if (d0Idx == -1) | |
d1Idx = component->metaObject()->indexOfProperty(cname); | |
if (d0Idx != -1) { | |
Instr instr; | |
instr.common.type = Instr::LoadScope; | |
instr.load.index = 0; | |
instr.load.reg = reg; | |
bytecode << instr; | |
subscribeName << contextName(); | |
subscribeName << name; | |
if (!fetch(type, context->metaObject(), reg, d0Idx, subscribeName, nameNodes.at(ii))) | |
return false; | |
} else if(d1Idx != -1) { | |
Instr instr; | |
instr.common.type = Instr::LoadRoot; | |
instr.load.index = 0; | |
instr.load.reg = reg; | |
bytecode << instr; | |
subscribeName << QLatin1String("$$$ROOT"); | |
subscribeName << name; | |
if (!fetch(type, component->metaObject(), reg, d1Idx, subscribeName, nameNodes.at(ii))) | |
return false; | |
} else if (qmlExperimental()) { | |
Instr find; | |
if (nameParts.count() == 1) | |
find.common.type = Instr::FindGenericTerminal; | |
else | |
find.common.type = Instr::FindGeneric; | |
find.find.reg = reg; | |
find.find.src = -1; | |
find.find.name = registerString(name); | |
find.find.exceptionId = exceptionId(nameNodes.at(ii)); | |
subscribeName << QString(QLatin1String("$$$Generic_") + name); | |
if (subscription(subscribeName, &type)) | |
find.find.subscribeIndex = subscriptionIndex(subscribeName); | |
else | |
find.find.subscribeIndex = -1; | |
bytecode << find; | |
type.unknownType = true; | |
} | |
if (!type.unknownType && type.type == -1) | |
return false; // Couldn't fetch that type | |
} | |
} else { | |
if (attachType) { | |
Instr attach; | |
attach.common.type = Instr::LoadAttached; | |
attach.attached.output = reg; | |
attach.attached.reg = reg; | |
attach.attached.id = attachType->attachedPropertiesId(); | |
bytecode << attach; | |
absType = 0; | |
type.metaObject = attachType->attachedPropertiesType(); | |
subscribeName << QLatin1String("$$$ATTACH_") + name; | |
continue; | |
} | |
const QMetaObject *mo = 0; | |
if (absType) | |
mo = absType->metaObject(); | |
else if (type.metaObject) | |
mo = type.metaObject; | |
QByteArray utf8Name = name.toUtf8(); | |
const char *cname = utf8Name.constData(); | |
int idx = mo?mo->indexOfProperty(cname):-1; | |
if (absType && idx == -1) | |
return false; | |
subscribeName << name; | |
if (absType || (wasAttachedObject && idx != -1) || (mo && mo->property(idx).isFinal())) { | |
absType = 0; | |
if (!fetch(type, mo, reg, idx, subscribeName, nameNodes.at(ii))) | |
return false; | |
} else { | |
Instr prop; | |
if (ii == nameParts.count() -1 ) | |
prop.common.type = Instr::FindPropertyTerminal; | |
else | |
prop.common.type = Instr::FindProperty; | |
prop.find.reg = reg; | |
prop.find.src = reg; | |
prop.find.name = registerString(name); | |
prop.find.exceptionId = exceptionId(nameNodes.at(ii)); | |
if (subscription(subscribeName, &type)) | |
prop.find.subscribeIndex = subscriptionIndex(subscribeName); | |
else | |
prop.find.subscribeIndex = -1; | |
type.unknownType = true; | |
type.metaObject = 0; | |
type.type = -1; | |
type.reg = reg; | |
bytecode << prop; | |
} | |
} | |
wasAttachedObject = false; | |
} | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::tryArith(QDeclarativeJS::AST::Node *node) | |
{ | |
if (node->kind != AST::Node::Kind_BinaryExpression) | |
return false; | |
AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node); | |
if (expression->op == QSOperator::Add || | |
expression->op == QSOperator::Sub) | |
return true; | |
else | |
return false; | |
} | |
bool QDeclarativeBindingCompilerPrivate::parseArith(QDeclarativeJS::AST::Node *node, Result &type) | |
{ | |
AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node); | |
type.reg = acquireReg(); | |
if (type.reg == -1) | |
return false; | |
Result lhs; | |
Result rhs; | |
if (!parseExpression(expression->left, lhs)) return false; | |
if (!parseExpression(expression->right, rhs)) return false; | |
if ((lhs.type == QVariant::Int || lhs.type == QMetaType::QReal) && | |
(rhs.type == QVariant::Int || rhs.type == QMetaType::QReal)) | |
return numberArith(type, lhs, rhs, (QSOperator::Op)expression->op); | |
else if(expression->op == QSOperator::Sub) | |
return numberArith(type, lhs, rhs, (QSOperator::Op)expression->op); | |
else if ((lhs.type == QMetaType::QString || lhs.unknownType) && | |
(rhs.type == QMetaType::QString || rhs.unknownType) && | |
(lhs.type == QMetaType::QString || rhs.type == QMetaType::QString)) | |
return stringArith(type, lhs, rhs, (QSOperator::Op)expression->op); | |
else | |
return false; | |
} | |
bool QDeclarativeBindingCompilerPrivate::numberArith(Result &type, const Result &lhs, const Result &rhs, QSOperator::Op op) | |
{ | |
bool nativeReal = rhs.type == QMetaType::QReal || | |
lhs.type == QMetaType::QReal || | |
lhs.unknownType || | |
rhs.unknownType; | |
if (nativeReal && lhs.type == QMetaType::Int) { | |
Instr convert; | |
convert.common.type = Instr::ConvertIntToReal; | |
convert.unaryop.output = lhs.reg; | |
convert.unaryop.src = lhs.reg; | |
bytecode << convert; | |
} | |
if (nativeReal && rhs.type == QMetaType::Int) { | |
Instr convert; | |
convert.common.type = Instr::ConvertIntToReal; | |
convert.unaryop.output = rhs.reg; | |
convert.unaryop.src = rhs.reg; | |
bytecode << convert; | |
} | |
int lhsTmp = -1; | |
int rhsTmp = -1; | |
if (lhs.unknownType) { | |
if (!qmlExperimental()) | |
return false; | |
lhsTmp = acquireReg(); | |
if (lhsTmp == -1) | |
return false; | |
Instr conv; | |
conv.common.type = Instr::ConvertGenericToReal; | |
conv.unaryop.output = lhsTmp; | |
conv.unaryop.src = lhs.reg; | |
bytecode << conv; | |
} | |
if (rhs.unknownType) { | |
if (!qmlExperimental()) | |
return false; | |
rhsTmp = acquireReg(); | |
if (rhsTmp == -1) | |
return false; | |
Instr conv; | |
conv.common.type = Instr::ConvertGenericToReal; | |
conv.unaryop.output = rhsTmp; | |
conv.unaryop.src = rhs.reg; | |
bytecode << conv; | |
} | |
Instr arith; | |
if (op == QSOperator::Add) { | |
arith.common.type = nativeReal?Instr::AddReal:Instr::AddInt; | |
} else if (op == QSOperator::Sub) { | |
arith.common.type = nativeReal?Instr::MinusReal:Instr::MinusInt; | |
} else { | |
qFatal("Unsupported arithmetic operator"); | |
} | |
arith.binaryop.output = type.reg; | |
arith.binaryop.src1 = (lhsTmp == -1)?lhs.reg:lhsTmp; | |
arith.binaryop.src2 = (rhsTmp == -1)?rhs.reg:rhsTmp; | |
bytecode << arith; | |
type.metaObject = 0; | |
type.type = nativeReal?QMetaType::QReal:QMetaType::Int; | |
type.subscriptionSet.unite(lhs.subscriptionSet); | |
type.subscriptionSet.unite(rhs.subscriptionSet); | |
if (lhsTmp != -1) releaseReg(lhsTmp); | |
if (rhsTmp != -1) releaseReg(rhsTmp); | |
releaseReg(lhs.reg); | |
releaseReg(rhs.reg); | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::stringArith(Result &type, const Result &lhs, const Result &rhs, QSOperator::Op op) | |
{ | |
if (op != QSOperator::Add) | |
return false; | |
int lhsTmp = -1; | |
int rhsTmp = -1; | |
if (lhs.unknownType) { | |
if (!qmlExperimental()) | |
return false; | |
lhsTmp = acquireReg(Instr::CleanupString); | |
if (lhsTmp == -1) | |
return false; | |
Instr convert; | |
convert.common.type = Instr::ConvertGenericToString; | |
convert.unaryop.output = lhsTmp; | |
convert.unaryop.src = lhs.reg; | |
bytecode << convert; | |
} | |
if (rhs.unknownType) { | |
if (!qmlExperimental()) | |
return false; | |
rhsTmp = acquireReg(Instr::CleanupString); | |
if (rhsTmp == -1) | |
return false; | |
Instr convert; | |
convert.common.type = Instr::ConvertGenericToString; | |
convert.unaryop.output = rhsTmp; | |
convert.unaryop.src = rhs.reg; | |
bytecode << convert; | |
} | |
type.reg = acquireReg(Instr::CleanupString); | |
if (type.reg == -1) | |
return false; | |
type.type = QMetaType::QString; | |
Instr add; | |
add.common.type = Instr::AddString; | |
add.binaryop.output = type.reg; | |
add.binaryop.src1 = (lhsTmp == -1)?lhs.reg:lhsTmp; | |
add.binaryop.src2 = (rhsTmp == -1)?rhs.reg:rhsTmp; | |
bytecode << add; | |
if (lhsTmp != -1) releaseReg(lhsTmp); | |
if (rhsTmp != -1) releaseReg(rhsTmp); | |
releaseReg(lhs.reg); | |
releaseReg(rhs.reg); | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::tryLogic(QDeclarativeJS::AST::Node *node) | |
{ | |
if (node->kind != AST::Node::Kind_BinaryExpression) | |
return false; | |
AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node); | |
if (expression->op == QSOperator::Gt || | |
expression->op == QSOperator::Equal || | |
expression->op == QSOperator::NotEqual) | |
return true; | |
else | |
return false; | |
} | |
bool QDeclarativeBindingCompilerPrivate::parseLogic(QDeclarativeJS::AST::Node *node, Result &type) | |
{ | |
AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node); | |
Result lhs; | |
Result rhs; | |
if (!parseExpression(expression->left, lhs)) return false; | |
if (!parseExpression(expression->right, rhs)) return false; | |
type.reg = acquireReg(); | |
if (type.reg == -1) | |
return false; | |
type.metaObject = 0; | |
type.type = QVariant::Bool; | |
if (lhs.type == QMetaType::QReal && rhs.type == QMetaType::QReal) { | |
Instr op; | |
if (expression->op == QSOperator::Gt) | |
op.common.type = Instr::GreaterThanReal; | |
else if (expression->op == QSOperator::Equal) | |
op.common.type = Instr::CompareReal; | |
else if (expression->op == QSOperator::NotEqual) | |
op.common.type = Instr::NotCompareReal; | |
else | |
return false; | |
op.binaryop.output = type.reg; | |
op.binaryop.src1 = lhs.reg; | |
op.binaryop.src2 = rhs.reg; | |
bytecode << op; | |
} else if (lhs.type == QMetaType::QString && rhs.type == QMetaType::QString) { | |
Instr op; | |
if (expression->op == QSOperator::Equal) | |
op.common.type = Instr::CompareString; | |
else if (expression->op == QSOperator::NotEqual) | |
op.common.type = Instr::NotCompareString; | |
else | |
return false; | |
op.binaryop.output = type.reg; | |
op.binaryop.src1 = lhs.reg; | |
op.binaryop.src2 = rhs.reg; | |
bytecode << op; | |
} else { | |
return false; | |
} | |
releaseReg(lhs.reg); | |
releaseReg(rhs.reg); | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::tryConditional(QDeclarativeJS::AST::Node *node) | |
{ | |
return (node->kind == AST::Node::Kind_ConditionalExpression); | |
} | |
bool QDeclarativeBindingCompilerPrivate::parseConditional(QDeclarativeJS::AST::Node *node, Result &type) | |
{ | |
AST::ConditionalExpression *expression = static_cast<AST::ConditionalExpression *>(node); | |
AST::Node *test = expression->expression; | |
if (test->kind == AST::Node::Kind_NestedExpression) | |
test = static_cast<AST::NestedExpression*>(test)->expression; | |
Result etype; | |
if (!parseExpression(test, etype)) return false; | |
if (etype.type != QVariant::Bool) | |
return false; | |
Instr skip; | |
skip.common.type = Instr::Skip; | |
skip.skip.reg = etype.reg; | |
skip.skip.count = 0; | |
int skipIdx = bytecode.count(); | |
bytecode << skip; | |
// Release to allow reuse of reg | |
releaseReg(etype.reg); | |
QSet<QString> preSubSet = subscriptionSet; | |
// int preConditionalSubscriptions = subscriptionSet.count(); | |
Result ok; | |
if (!parseExpression(expression->ok, ok)) return false; | |
if (ok.unknownType) return false; | |
int skipIdx2 = bytecode.count(); | |
skip.skip.reg = -1; | |
bytecode << skip; | |
// Release to allow reuse of reg | |
releaseReg(ok.reg); | |
bytecode[skipIdx].skip.count = bytecode.count() - skipIdx - 1; | |
subscriptionSet = preSubSet; | |
Result ko; | |
if (!parseExpression(expression->ko, ko)) return false; | |
if (ko.unknownType) return false; | |
// Release to allow reuse of reg | |
releaseReg(ko.reg); | |
bytecode[skipIdx2].skip.count = bytecode.count() - skipIdx2 - 1; | |
if (ok != ko) | |
return false; // Must be same type and in same register | |
subscriptionSet = preSubSet; | |
if (!subscriptionNeutral(subscriptionSet, ok.subscriptionSet, ko.subscriptionSet)) | |
return false; // Conditionals cannot introduce new subscriptions | |
type = ok; | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::tryConstant(QDeclarativeJS::AST::Node *node) | |
{ | |
return node->kind == AST::Node::Kind_TrueLiteral || | |
node->kind == AST::Node::Kind_FalseLiteral || | |
node->kind == AST::Node::Kind_NumericLiteral || | |
node->kind == AST::Node::Kind_StringLiteral; | |
} | |
bool QDeclarativeBindingCompilerPrivate::parseConstant(QDeclarativeJS::AST::Node *node, Result &type) | |
{ | |
type.metaObject = 0; | |
type.type = -1; | |
type.reg = acquireReg(); | |
if (type.reg == -1) | |
return false; | |
if (node->kind == AST::Node::Kind_TrueLiteral) { | |
type.type = QVariant::Bool; | |
Instr instr; | |
instr.common.type = Instr::Bool; | |
instr.bool_value.reg = type.reg; | |
instr.bool_value.value = true; | |
bytecode << instr; | |
return true; | |
} else if (node->kind == AST::Node::Kind_FalseLiteral) { | |
type.type = QVariant::Bool; | |
Instr instr; | |
instr.common.type = Instr::Bool; | |
instr.bool_value.reg = type.reg; | |
instr.bool_value.value = false; | |
bytecode << instr; | |
return true; | |
} else if (node->kind == AST::Node::Kind_NumericLiteral) { | |
qreal value = qreal(static_cast<AST::NumericLiteral *>(node)->value); | |
if (qreal(float(value)) != value) | |
return false; | |
type.type = QMetaType::QReal; | |
Instr instr; | |
instr.common.type = Instr::Real; | |
instr.real_value.reg = type.reg; | |
instr.real_value.value = float(value); | |
bytecode << instr; | |
return true; | |
} else if (node->kind == AST::Node::Kind_StringLiteral) { | |
QString str = static_cast<AST::StringLiteral *>(node)->value->asString(); | |
type.type = QMetaType::QString; | |
type.reg = registerLiteralString(str); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
bool QDeclarativeBindingCompilerPrivate::tryMethod(QDeclarativeJS::AST::Node *node) | |
{ | |
return node->kind == AST::Node::Kind_CallExpression; | |
} | |
bool QDeclarativeBindingCompilerPrivate::parseMethod(QDeclarativeJS::AST::Node *node, Result &result) | |
{ | |
AST::CallExpression *expr = static_cast<AST::CallExpression *>(node); | |
QStringList name; | |
if (!buildName(name, expr->base)) | |
return false; | |
if (name.count() != 2 || name.at(0) != QLatin1String("Math")) | |
return false; | |
QString method = name.at(1); | |
AST::ArgumentList *args = expr->arguments; | |
if (!args) return false; | |
AST::ExpressionNode *arg0 = args->expression; | |
args = args->next; | |
if (!args) return false; | |
AST::ExpressionNode *arg1 = args->expression; | |
if (args->next != 0) return false; | |
if (!arg0 || !arg1) return false; | |
Result r0; | |
if (!parseExpression(arg0, r0)) return false; | |
Result r1; | |
if (!parseExpression(arg1, r1)) return false; | |
if (r0.type != QMetaType::QReal || r1.type != QMetaType::QReal) | |
return false; | |
Instr op; | |
if (method == QLatin1String("max")) { | |
op.common.type = Instr::MaxReal; | |
} else if (method == QLatin1String("min")) { | |
op.common.type = Instr::MinReal; | |
} else { | |
return false; | |
} | |
// We release early to reuse registers | |
releaseReg(r0.reg); | |
releaseReg(r1.reg); | |
op.binaryop.output = acquireReg(); | |
if (op.binaryop.output == -1) | |
return false; | |
op.binaryop.src1 = r0.reg; | |
op.binaryop.src2 = r1.reg; | |
bytecode << op; | |
result.type = QMetaType::QReal; | |
result.reg = op.binaryop.output; | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::buildName(QStringList &name, | |
QDeclarativeJS::AST::Node *node, | |
QList<QDeclarativeJS::AST::ExpressionNode *> *nodes) | |
{ | |
if (node->kind == AST::Node::Kind_IdentifierExpression) { | |
name << static_cast<AST::IdentifierExpression*>(node)->name->asString(); | |
if (nodes) *nodes << static_cast<AST::IdentifierExpression*>(node); | |
} else if (node->kind == AST::Node::Kind_FieldMemberExpression) { | |
AST::FieldMemberExpression *expr = | |
static_cast<AST::FieldMemberExpression *>(node); | |
if (!buildName(name, expr->base, nodes)) | |
return false; | |
name << expr->name->asString(); | |
if (nodes) *nodes << expr; | |
} else { | |
return false; | |
} | |
return true; | |
} | |
bool QDeclarativeBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg, | |
int idx, const QStringList &subName, | |
QDeclarativeJS::AST::ExpressionNode *node) | |
{ | |
QMetaProperty prop = mo->property(idx); | |
rv.metaObject = 0; | |
rv.type = 0; | |
//XXX binding optimizer doesn't handle properties with a revision | |
if (prop.revision() > 0) | |
return false; | |
int fastFetchIndex = fastProperties()->accessorIndexForProperty(mo, idx); | |
Instr fetch; | |
if (!qmlDisableFastProperties() && fastFetchIndex != -1) { | |
fetch.common.type = Instr::FetchAndSubscribe; | |
fetch.fetchAndSubscribe.objectReg = reg; | |
fetch.fetchAndSubscribe.output = reg; | |
fetch.fetchAndSubscribe.function = fastFetchIndex; | |
fetch.fetchAndSubscribe.subscription = subscriptionIndex(subName); | |
fetch.fetchAndSubscribe.exceptionId = exceptionId(node); | |
} else { | |
if (subscription(subName, &rv) && prop.hasNotifySignal() && prop.notifySignalIndex() != -1) { | |
Instr sub; | |
sub.common.type = Instr::Subscribe; | |
sub.subscribe.offset = subscriptionIndex(subName); | |
sub.subscribe.reg = reg; | |
sub.subscribe.index = prop.notifySignalIndex(); | |
bytecode << sub; | |
} | |
fetch.common.type = Instr::Fetch; | |
fetch.fetch.objectReg = reg; | |
fetch.fetch.index = idx; | |
fetch.fetch.output = reg; | |
fetch.fetch.exceptionId = exceptionId(node); | |
} | |
rv.type = prop.userType(); | |
rv.metaObject = engine->metaObjectForType(rv.type); | |
rv.reg = reg; | |
if (rv.type == QMetaType::QString) { | |
int tmp = acquireReg(); | |
if (tmp == -1) | |
return false; | |
Instr copy; | |
copy.common.type = Instr::Copy; | |
copy.copy.reg = tmp; | |
copy.copy.src = reg; | |
bytecode << copy; | |
releaseReg(tmp); | |
fetch.fetch.objectReg = tmp; | |
Instr setup; | |
setup.common.type = Instr::NewString; | |
setup.construct.reg = reg; | |
bytecode << setup; | |
registerCleanup(reg, Instr::CleanupString); | |
} | |
bytecode << fetch; | |
if (!rv.metaObject && | |
rv.type != QMetaType::QReal && | |
rv.type != QMetaType::Int && | |
rv.type != QMetaType::Bool && | |
rv.type != qMetaTypeId<QDeclarativeAnchorLine>() && | |
rv.type != QMetaType::QString) { | |
rv.metaObject = 0; | |
rv.type = 0; | |
return false; // Unsupported type (string not supported yet); | |
} | |
return true; | |
} | |
void QDeclarativeBindingCompilerPrivate::registerCleanup(int reg, int cleanup, int cleanupType) | |
{ | |
registerCleanups.insert(reg, qMakePair(cleanup, cleanupType)); | |
} | |
int QDeclarativeBindingCompilerPrivate::acquireReg(int cleanup, int cleanupType) | |
{ | |
for (int ii = 0; ii < 32; ++ii) { | |
if (!(registers & (1 << ii))) { | |
registers |= (1 << ii); | |
if (cleanup != Instr::Noop) | |
registerCleanup(ii, cleanup, cleanupType); | |
return ii; | |
} | |
} | |
return -1; | |
} | |
void QDeclarativeBindingCompilerPrivate::releaseReg(int reg) | |
{ | |
Q_ASSERT(reg >= 0 && reg <= 31); | |
if (registerCleanups.contains(reg)) { | |
QPair<int, int> c = registerCleanups[reg]; | |
registerCleanups.remove(reg); | |
Instr cleanup; | |
cleanup.common.type = (quint8)c.first; | |
cleanup.cleanup.reg = reg; | |
bytecode << cleanup; | |
} | |
quint32 mask = 1 << reg; | |
registers &= ~mask; | |
} | |
// Returns a reg | |
int QDeclarativeBindingCompilerPrivate::registerLiteralString(const QString &str) | |
{ | |
QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); | |
int offset = data.count(); | |
data += strdata; | |
int reg = acquireReg(Instr::CleanupString); | |
if (reg == -1) | |
return false; | |
Instr string; | |
string.common.type = Instr::String; | |
string.string_value.reg = reg; | |
string.string_value.offset = offset; | |
string.string_value.length = str.length(); | |
bytecode << string; | |
return reg; | |
} | |
// Returns an identifier offset | |
int QDeclarativeBindingCompilerPrivate::registerString(const QString &string) | |
{ | |
Q_ASSERT(!string.isEmpty()); | |
QHash<QString, QPair<int, int> >::ConstIterator iter = registeredStrings.find(string); | |
if (iter == registeredStrings.end()) { | |
quint32 len = string.length(); | |
QByteArray lendata((const char *)&len, sizeof(quint32)); | |
QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar)); | |
strdata.prepend(lendata); | |
int rv = data.count(); | |
data += strdata; | |
iter = registeredStrings.insert(string, qMakePair(registeredStrings.count(), rv)); | |
} | |
Instr reg; | |
reg.common.type = Instr::InitString; | |
reg.initstring.offset = iter->first; | |
reg.initstring.dataIdx = iter->second; | |
bytecode << reg; | |
return reg.initstring.offset; | |
} | |
bool QDeclarativeBindingCompilerPrivate::subscription(const QStringList &sub, Result *result) | |
{ | |
QString str = sub.join(QLatin1String(".")); | |
result->subscriptionSet.insert(str); | |
if (subscriptionSet.contains(str)) { | |
return false; | |
} else { | |
subscriptionSet.insert(str); | |
return true; | |
} | |
} | |
int QDeclarativeBindingCompilerPrivate::subscriptionIndex(const QStringList &sub) | |
{ | |
QString str = sub.join(QLatin1String(".")); | |
QHash<QString, int>::ConstIterator iter = subscriptionIds.find(str); | |
if (iter == subscriptionIds.end()) | |
iter = subscriptionIds.insert(str, subscriptionIds.count()); | |
usedSubscriptionIds.insert(*iter); | |
return *iter; | |
} | |
/* | |
Returns true if lhs contains no subscriptions that aren't also in base or rhs AND | |
rhs contains no subscriptions that aren't also in base or lhs. | |
*/ | |
bool QDeclarativeBindingCompilerPrivate::subscriptionNeutral(const QSet<QString> &base, | |
const QSet<QString> &lhs, | |
const QSet<QString> &rhs) | |
{ | |
QSet<QString> difflhs = lhs; | |
difflhs.subtract(rhs); | |
QSet<QString> diffrhs = rhs; | |
diffrhs.subtract(lhs); | |
difflhs.unite(diffrhs); | |
difflhs.subtract(base); | |
return difflhs.isEmpty(); | |
} | |
quint8 QDeclarativeBindingCompilerPrivate::exceptionId(QDeclarativeJS::AST::ExpressionNode *n) | |
{ | |
quint8 rv = 0xFF; | |
if (n && exceptions.count() < 0xFF) { | |
rv = (quint8)exceptions.count(); | |
QDeclarativeJS::AST::SourceLocation l = n->firstSourceLocation(); | |
quint64 e = l.startLine; | |
e <<= 32; | |
e |= l.startColumn; | |
exceptions.append(e); | |
} | |
return rv; | |
} | |
QDeclarativeBindingCompiler::QDeclarativeBindingCompiler() | |
: d(new QDeclarativeBindingCompilerPrivate) | |
{ | |
} | |
QDeclarativeBindingCompiler::~QDeclarativeBindingCompiler() | |
{ | |
delete d; d = 0; | |
} | |
/* | |
Returns true if any bindings were compiled. | |
*/ | |
bool QDeclarativeBindingCompiler::isValid() const | |
{ | |
return !d->committed.bytecode.isEmpty(); | |
} | |
/* | |
-1 on failure, otherwise the binding index to use. | |
*/ | |
int QDeclarativeBindingCompiler::compile(const Expression &expression, QDeclarativeEnginePrivate *engine) | |
{ | |
if (!expression.expression.asAST()) return false; | |
if (!qmlExperimental() && expression.property->isValueTypeSubProperty) | |
return -1; | |
if (qmlDisableOptimizer()) | |
return -1; | |
d->context = expression.context; | |
d->component = expression.component; | |
d->destination = expression.property; | |
d->ids = expression.ids; | |
d->imports = expression.imports; | |
d->engine = engine; | |
if (d->compile(expression.expression.asAST())) { | |
return d->commitCompile(); | |
} else { | |
return -1; | |
} | |
} | |
QByteArray QDeclarativeBindingCompilerPrivate::buildSignalTable() const | |
{ | |
QHash<int, QList<int> > table; | |
for (int ii = 0; ii < committed.count(); ++ii) { | |
const QSet<int> &deps = committed.dependencies.at(ii); | |
for (QSet<int>::ConstIterator iter = deps.begin(); iter != deps.end(); ++iter) | |
table[*iter].append(ii); | |
} | |
QVector<quint32> header; | |
QVector<quint32> data; | |
for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { | |
header.append(committed.subscriptionIds.count() + data.count()); | |
const QList<int> &bindings = table[ii]; | |
data.append(bindings.count()); | |
for (int jj = 0; jj < bindings.count(); ++jj) | |
data.append(bindings.at(jj)); | |
} | |
header << data; | |
return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32)); | |
} | |
QByteArray QDeclarativeBindingCompilerPrivate::buildExceptionData() const | |
{ | |
QByteArray rv; | |
rv.resize(committed.exceptions.count() * sizeof(quint64)); | |
::memcpy(rv.data(), committed.exceptions.constData(), rv.size()); | |
return rv; | |
} | |
/* | |
Returns the compiled program. | |
*/ | |
QByteArray QDeclarativeBindingCompiler::program() const | |
{ | |
QByteArray programData; | |
if (isValid()) { | |
Program prog; | |
prog.bindings = d->committed.count(); | |
QVector<Instr> bytecode; | |
Instr skip; | |
skip.common.type = Instr::Skip; | |
skip.skip.reg = -1; | |
for (int ii = 0; ii < d->committed.count(); ++ii) { | |
skip.skip.count = d->committed.count() - ii - 1; | |
skip.skip.count+= d->committed.offsets.at(ii); | |
bytecode << skip; | |
} | |
bytecode << d->committed.bytecode; | |
QByteArray data = d->committed.data; | |
while (data.count() % 4) data.append('\0'); | |
prog.signalTableOffset = data.count(); | |
data += d->buildSignalTable(); | |
while (data.count() % 4) data.append('\0'); | |
prog.exceptionDataOffset = data.count(); | |
data += d->buildExceptionData(); | |
prog.dataLength = 4 * ((data.size() + 3) / 4); | |
prog.subscriptions = d->committed.subscriptionIds.count(); | |
prog.identifiers = d->committed.registeredStrings.count(); | |
prog.instructionCount = bytecode.count(); | |
prog.compiled = false; | |
int size = sizeof(Program) + bytecode.count() * sizeof(Instr); | |
size += prog.dataLength; | |
programData.resize(size); | |
memcpy(programData.data(), &prog, sizeof(Program)); | |
if (prog.dataLength) | |
memcpy((char *)((Program *)programData.data())->data(), data.constData(), | |
data.size()); | |
memcpy((char *)((Program *)programData.data())->instructions(), bytecode.constData(), | |
bytecode.count() * sizeof(Instr)); | |
} | |
return programData; | |
} | |
QT_END_NAMESPACE |