diff options
Diffstat (limited to 'src/declarative/qml')
64 files changed, 9329 insertions, 5611 deletions
diff --git a/src/declarative/qml/qdeclarative.h b/src/declarative/qml/qdeclarative.h index 5da7901528..9227260b64 100644 --- a/src/declarative/qml/qdeclarative.h +++ b/src/declarative/qml/qdeclarative.h @@ -53,6 +53,9 @@ QT_BEGIN_HEADER +#define QML_VERSION 0x020000 +#define QML_VERSION_STR "2.0" + #define QML_DECLARE_TYPE(TYPE) \ Q_DECLARE_METATYPE(TYPE *) \ Q_DECLARE_METATYPE(QDeclarativeListProperty<TYPE>) @@ -392,6 +395,8 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor, class QDeclarativeContext; class QDeclarativeEngine; +class QScriptValue; +class QScriptEngine; Q_DECLARATIVE_EXPORT void qmlExecuteDeferred(QObject *); Q_DECLARATIVE_EXPORT QDeclarativeContext *qmlContext(const QObject *); Q_DECLARATIVE_EXPORT QDeclarativeEngine *qmlEngine(const QObject *); @@ -405,6 +410,34 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true) return qmlAttachedPropertiesObject(&idx, obj, &T::staticMetaObject, create); } +inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMinor, + QScriptValue (*callback)(QDeclarativeEngine *, QScriptEngine *)) +{ + QDeclarativePrivate::RegisterModuleApi api = { + 0, + + uri, versionMajor, versionMinor, + + callback, 0 + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::ModuleApiRegistration, &api); +} + +inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMinor, + QObject *(*callback)(QDeclarativeEngine *, QScriptEngine *)) +{ + QDeclarativePrivate::RegisterModuleApi api = { + 0, + + uri, versionMajor, versionMinor, + + 0, callback + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::ModuleApiRegistration, &api); +} + QT_END_NAMESPACE QML_DECLARE_TYPE(QObject) diff --git a/src/declarative/qml/qdeclarativebinding.cpp b/src/declarative/qml/qdeclarativebinding.cpp index a5bd604c53..3e93ce7266 100644 --- a/src/declarative/qml/qdeclarativebinding.cpp +++ b/src/declarative/qml/qdeclarativebinding.cpp @@ -357,13 +357,17 @@ void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags) } else { QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(d->context()->engine); + ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. bool isUndefined = false; QVariant value; QScriptValue scriptValue = d->scriptValue(0, &isUndefined); - if (wasDeleted) + + if (wasDeleted) { + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. return; + } if (d->property.propertyTypeCategory() == QDeclarativeProperty::List) { value = ep->scriptValueToVariant(scriptValue, qMetaTypeId<QList<QObject *> >()); @@ -420,8 +424,10 @@ void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags) } else if (d->property.object() && !QDeclarativePropertyPrivate::write(d->property, value, flags)) { - if (wasDeleted) + if (wasDeleted) { + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. return; + } QUrl url = QUrl(d->url); int line = d->line; @@ -440,14 +446,21 @@ void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags) QLatin1String(QMetaType::typeName(d->property.propertyType()))); } - if (wasDeleted) + if (wasDeleted) { + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. return; + } if (d->error.isValid()) { if (!d->addError(ep)) ep->warning(this->error()); } else { d->removeError(); } + + // at this point, the binding has been evaluated. If any scarce + // resources were copied during the evaluation of the binding, + // we need to release those copies. + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } d->updating = false; diff --git a/src/declarative/qml/qdeclarativecompiledbindings.cpp b/src/declarative/qml/qdeclarativecompiledbindings.cpp deleted file mode 100644 index a6fcce4c99..0000000000 --- a/src/declarative/qml/qdeclarativecompiledbindings.cpp +++ /dev/null @@ -1,2906 +0,0 @@ -/**************************************************************************** -** -** 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$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, 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. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $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 diff --git a/src/declarative/qml/qdeclarativecompileddata.cpp b/src/declarative/qml/qdeclarativecompileddata.cpp index 03deea1e36..3d1e9255ad 100644 --- a/src/declarative/qml/qdeclarativecompileddata.cpp +++ b/src/declarative/qml/qdeclarativecompileddata.cpp @@ -181,6 +181,9 @@ QDeclarativeCompiledData::~QDeclarativeCompiledData() for (int ii = 0; ii < contextCaches.count(); ++ii) contextCaches.at(ii)->release(); + for (int ii = 0; ii < scripts.count(); ++ii) + scripts.at(ii)->release(); + if (importCache) importCache->release(); diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index f57f842004..d325ac48dd 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -63,7 +63,7 @@ #include "private/qdeclarativeglobal_p.h" #include "private/qdeclarativescriptparser_p.h" #include "private/qdeclarativebinding_p.h" -#include "private/qdeclarativecompiledbindings_p.h" +#include "private/qdeclarativev4compiler_p.h" #include "private/qdeclarativeglobalscriptclass_p.h" #include <QColor> @@ -79,7 +79,6 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP); DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS); -DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP); using namespace QDeclarativeParser; @@ -201,6 +200,9 @@ bool QDeclarativeCompiler::testLiteralAssignment(const QMetaProperty &prop, case QVariant::String: if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string expected")); break; + case QVariant::ByteArray: + if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: byte array expected")); + break; case QVariant::Url: if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: url expected")); break; @@ -320,16 +322,20 @@ bool QDeclarativeCompiler::testLiteralAssignment(const QMetaProperty &prop, void QDeclarativeCompiler::genLiteralAssignment(const QMetaProperty &prop, QDeclarativeParser::Value *v) { - QString string = v->value.asString(); - QDeclarativeInstruction instr; instr.line = v->location.start.line; if (prop.isEnumType()) { int value; - if (prop.isFlagType()) { - value = prop.enumerator().keysToValue(string.toUtf8().constData()); - } else - value = prop.enumerator().keyToValue(string.toUtf8().constData()); + if (v->value.isNumber()) { + // Preresolved enum + value = (int)v->value.asNumber(); + } else { + // Must be a string + if (prop.isFlagType()) { + value = prop.enumerator().keysToValue(v->value.asString().toUtf8().constData()); + } else + value = prop.enumerator().keyToValue(v->value.asString().toUtf8().constData()); + } instr.type = QDeclarativeInstruction::StoreInteger; instr.storeInteger.propertyIndex = prop.propertyIndex(); @@ -338,6 +344,8 @@ void QDeclarativeCompiler::genLiteralAssignment(const QMetaProperty &prop, return; } + QString string = v->value.asString(); + int type = prop.userType(); switch(type) { case -1: @@ -371,6 +379,13 @@ void QDeclarativeCompiler::genLiteralAssignment(const QMetaProperty &prop, instr.storeString.value = output->indexForString(string); } break; + case QVariant::ByteArray: + { + instr.type = QDeclarativeInstruction::StoreByteArray; + instr.storeByteArray.propertyIndex = prop.propertyIndex(); + instr.storeByteArray.value = output->indexForByteArray(string.toLatin1()); + } + break; case QVariant::Url: { instr.type = QDeclarativeInstruction::StoreUrl; @@ -642,6 +657,31 @@ void QDeclarativeCompiler::compileTree(QDeclarativeParser::Object *tree) compileState.root = tree; componentStat.lineNumber = tree->location.start.line; + // Build global import scripts + QStringList importedScriptIndexes; + + foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) { + importedScriptIndexes.append(script.qualifier); + + QDeclarativeInstruction import; + import.type = QDeclarativeInstruction::StoreImportedScript; + import.line = 0; + import.storeScript.value = output->scripts.count(); + + QDeclarativeScriptData *scriptData = script.script->scriptData(); + scriptData->addref(); + output->scripts << scriptData; + output->bytecode << import; + } + + // We generate the importCache before we build the tree so that + // it can be used in the binding compiler. Given we "expect" the + // QML compilation to succeed, this isn't a waste. + output->importCache = new QDeclarativeTypeNameCache(engine); + for (int ii = 0; ii < importedScriptIndexes.count(); ++ii) + output->importCache->add(importedScriptIndexes.at(ii), ii); + unit->imports().populateCache(output->importCache, engine); + if (!buildObject(tree, BindingContext()) || !completeComponentBuild()) return; @@ -657,38 +697,6 @@ void QDeclarativeCompiler::compileTree(QDeclarativeParser::Object *tree) init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData); output->bytecode << init; - // Build global import scripts - QHash<QString, Object::ScriptBlock> importedScripts; - QStringList importedScriptIndexes; - - foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) { - QString scriptCode = script.script->scriptSource(); - Object::ScriptBlock::Pragmas pragmas = script.script->pragmas(); - - Q_ASSERT(!importedScripts.contains(script.qualifier)); - - if (!scriptCode.isEmpty()) { - Object::ScriptBlock &scriptBlock = importedScripts[script.qualifier]; - - scriptBlock.code = scriptCode; - scriptBlock.file = script.script->finalUrl().toString(); - scriptBlock.pragmas = pragmas; - } - } - - for (QHash<QString, Object::ScriptBlock>::Iterator iter = importedScripts.begin(); - iter != importedScripts.end(); ++iter) { - - importedScriptIndexes.append(iter.key()); - - QDeclarativeInstruction import; - import.type = QDeclarativeInstruction::StoreImportedScript; - import.line = 0; - import.storeScript.value = output->scripts.count(); - output->scripts << *iter; - output->bytecode << import; - } - genObject(tree); QDeclarativeInstruction def; @@ -696,13 +704,6 @@ void QDeclarativeCompiler::compileTree(QDeclarativeParser::Object *tree) def.type = QDeclarativeInstruction::SetDefault; output->bytecode << def; - output->importCache = new QDeclarativeTypeNameCache(engine); - - for (int ii = 0; ii < importedScriptIndexes.count(); ++ii) - output->importCache->add(importedScriptIndexes.at(ii), ii); - - unit->imports().populateCache(output->importCache, engine); - Q_ASSERT(tree->metatype); if (tree->metadata.isEmpty()) { @@ -1283,6 +1284,7 @@ bool QDeclarativeCompiler::buildComponentFromRoot(QDeclarativeParser::Object *ob compileState = ComponentCompileState(); compileState.root = obj; + compileState.nested = true; componentStat = ComponentStat(); componentStat.lineNumber = obj->location.start.line; @@ -1442,8 +1444,6 @@ bool QDeclarativeCompiler::buildProperty(QDeclarativeParser::Property *prop, unit->imports().resolveType(prop->name, &type, 0, 0, 0, &typeNamespace); if (typeNamespace) { - // ### We might need to indicate that this property is a namespace - // for the DOM API COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj, ctxt)); return true; @@ -2227,20 +2227,35 @@ bool QDeclarativeCompiler::testQualifiedEnumAssignment(const QMetaProperty &prop objTypeName = objType->qmlTypeName(); } - if (!type || objTypeName != type->qmlTypeName()) + if (!type) return true; QString enumValue = parts.at(1); - int value; - if (prop.isFlagType()) { - value = prop.enumerator().keysToValue(enumValue.toUtf8().constData()); - } else - value = prop.enumerator().keyToValue(enumValue.toUtf8().constData()); + int value = -1; + + if (objTypeName == type->qmlTypeName()) { + // When these two match, we can short cut the search + if (prop.isFlagType()) { + value = prop.enumerator().keysToValue(enumValue.toUtf8().constData()); + } else { + value = prop.enumerator().keyToValue(enumValue.toUtf8().constData()); + } + } else { + // Otherwise we have to search the whole type + // This matches the logic in QDeclarativeTypeNameScriptClass + QByteArray enumName = enumValue.toUtf8(); + const QMetaObject *metaObject = type->baseMetaObject(); + for (int ii = metaObject->enumeratorCount() - 1; value == -1 && ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + value = e.keyToValue(enumName.constData()); + } + } + if (value == -1) return true; v->type = Value::Literal; - v->value = QDeclarativeParser::Variant(enumValue); + v->value = QDeclarativeParser::Variant((double)value); *isAssignment = true; return true; @@ -2412,7 +2427,7 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeParser::Object *obj, Dyn newClassName.append("_QML_"); int idx = classIndexCounter()->fetchAndAddRelaxed(1); newClassName.append(QByteArray::number(idx)); - if (compileState.root == obj) { + if (compileState.root == obj && !compileState.nested) { QString path = output->url.path(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); if (lastSlash > -1) { @@ -2888,25 +2903,26 @@ bool QDeclarativeCompiler::completeComponentBuild() COMPILE_CHECK(buildDynamicMeta(aliasObject, ResolveAliases)); } - QDeclarativeBindingCompiler::Expression expr; + QDeclarativeV4Compiler::Expression expr; expr.component = compileState.root; expr.ids = compileState.ids; + expr.importCache = output->importCache; + expr.imports = unit->imports(); - QDeclarativeBindingCompiler bindingCompiler; + QDeclarativeV4Compiler bindingCompiler; for (QHash<QDeclarativeParser::Value*,BindingReference>::Iterator iter = compileState.bindings.begin(); iter != compileState.bindings.end(); ++iter) { BindingReference &binding = *iter; - expr.context = binding.bindingContext.object; - expr.property = binding.property; - expr.expression = binding.expression; - expr.imports = unit->imports(); - // ### We don't currently optimize for bindings on alias's - because // of the solution to QTBUG-13719 if (!binding.property->isAlias) { + expr.context = binding.bindingContext.object; + expr.property = binding.property; + expr.expression = binding.expression; + int index = bindingCompiler.compile(expr, enginePrivate); if (index != -1) { binding.dataType = BindingReference::Experimental; @@ -2947,11 +2963,8 @@ bool QDeclarativeCompiler::completeComponentBuild() componentStat.scriptBindings.append(iter.key()->location); } - if (bindingCompiler.isValid()) { + if (bindingCompiler.isValid()) compileState.compiledBindingData = bindingCompiler.program(); - if (bindingsDump()) - QDeclarativeBindingCompiler::dump(compileState.compiledBindingData); - } saveComponentState(); diff --git a/src/declarative/qml/qdeclarativecompiler_p.h b/src/declarative/qml/qdeclarativecompiler_p.h index 93b6a0961e..49bab759be 100644 --- a/src/declarative/qml/qdeclarativecompiler_p.h +++ b/src/declarative/qml/qdeclarativecompiler_p.h @@ -122,7 +122,7 @@ public: QList<QScriptValue *> cachedClosures; QList<QDeclarativePropertyCache *> propertyCaches; QList<QDeclarativeIntegerCache *> contextCaches; - QList<QDeclarativeParser::Object::ScriptBlock> scripts; + QList<QDeclarativeScriptData *> scripts; QList<QUrl> urls; void dumpInstructions(); @@ -305,11 +305,12 @@ private: struct ComponentCompileState { ComponentCompileState() - : parserStatusCount(0), pushedProperties(0), root(0) {} + : parserStatusCount(0), pushedProperties(0), nested(false), root(0) {} QHash<QString, QDeclarativeParser::Object *> ids; QHash<int, QDeclarativeParser::Object *> idIndexes; int parserStatusCount; int pushedProperties; + bool nested; QByteArray compiledBindingData; diff --git a/src/declarative/qml/qdeclarativecomponent.cpp b/src/declarative/qml/qdeclarativecomponent.cpp index 8238252db2..aa1bbd156f 100644 --- a/src/declarative/qml/qdeclarativecomponent.cpp +++ b/src/declarative/qml/qdeclarativecomponent.cpp @@ -836,6 +836,34 @@ QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context, cons return begin(context, creationContext, cc, start, count, &state, 0, bindings); } +/* + Try to do what's necessary for a reasonable display of the type + name, but no more (just enough for the client to do more extensive cleanup). + + Should only be called when debugging is enabled. +*/ +static inline QString buildTypeNameForDebug(const QMetaObject *metaObject) +{ + static const QString qmlMarker(QLatin1String("_QML")); + static const QChar underscore(QLatin1Char('_')); + static const QChar asterisk(QLatin1Char('*')); + QDeclarativeType *type = QDeclarativeMetaType::qmlType(metaObject); + QString typeName = type ? QLatin1String(type->qmlTypeName()) : QLatin1String(metaObject->className()); + if (!type) { + //### optimize further? + int marker = typeName.indexOf(qmlMarker); + if (marker != -1 && marker < typeName.count() - 1) { + if (typeName[marker + 1] == underscore) { + const QString className = typeName.left(marker) + asterisk; + type = QDeclarativeMetaType::qmlType(QMetaType::type(className.toLatin1())); + if (type) + typeName = QLatin1String(type->qmlTypeName()); + } + } + } + return typeName; +} + QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentContext, QDeclarativeContextData *componentCreationContext, QDeclarativeCompiledData *component, int start, int count, @@ -848,10 +876,8 @@ QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentCon Q_ASSERT(!isRoot || state); // Either this isn't a root component, or a state data must be provided Q_ASSERT((state != 0) ^ (errors != 0)); // One of state or errors (but not both) must be provided - if (isRoot) { + if (isRoot) QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Creating); - QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating, component->url); - } QDeclarativeContextData *ctxt = new QDeclarativeContextData; ctxt->isInternal = true; @@ -868,7 +894,9 @@ QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentCon enginePriv->inBeginCreate = true; QDeclarativeVME vme; + enginePriv->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. QObject *rv = vme.run(ctxt, component, start, count, bindings); + enginePriv->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. if (vme.isError()) { if(errors) *errors = vme.errors(); @@ -897,6 +925,11 @@ QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentCon if (!parentContext->isInternal) parentContext->asQDeclarativeContextPrivate()->instances.append(rv); QDeclarativeEngineDebugServer::instance()->objectCreated(parentContext->engine, rv); + if (isRoot) { + QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating, buildTypeNameForDebug(rv->metaObject())); + QDeclarativeData *data = QDeclarativeData::get(rv); + QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Creating, component->url, data ? data->lineNumber : -1); + } } return rv; diff --git a/src/declarative/qml/qdeclarativecomponent.h b/src/declarative/qml/qdeclarativecomponent.h index 9a60a8b4f7..f46ffdf2cf 100644 --- a/src/declarative/qml/qdeclarativecomponent.h +++ b/src/declarative/qml/qdeclarativecomponent.h @@ -117,7 +117,6 @@ private: Q_DISABLE_COPY(QDeclarativeComponent) friend class QDeclarativeVME; - friend class QDeclarativeCompositeTypeData; friend class QDeclarativeTypeData; }; diff --git a/src/declarative/qml/qdeclarativecontext.cpp b/src/declarative/qml/qdeclarativecontext.cpp index 7637b72eb3..4f0d704d95 100644 --- a/src/declarative/qml/qdeclarativecontext.cpp +++ b/src/declarative/qml/qdeclarativecontext.cpp @@ -46,9 +46,9 @@ #include "private/qdeclarativeexpression_p.h" #include "private/qdeclarativeengine_p.h" #include "qdeclarativeengine.h" -#include "private/qdeclarativecompiledbindings_p.h" #include "qdeclarativeinfo.h" #include "private/qdeclarativeglobalscriptclass_p.h" +#include "private/qdeclarativev4bindings_p.h" #include <qscriptengine.h> #include <QtCore/qvarlengtharray.h> @@ -498,7 +498,7 @@ QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListProperty<QObject QDeclarativeContextData::QDeclarativeContextData() -: parent(0), engine(0), isInternal(false), publicContext(0), propertyNames(0), contextObject(0), +: parent(0), engine(0), isInternal(false), ownedByParent(false), publicContext(0), propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), optimizedBindings(0), linkedContext(0), componentAttached(0) @@ -506,7 +506,7 @@ QDeclarativeContextData::QDeclarativeContextData() } QDeclarativeContextData::QDeclarativeContextData(QDeclarativeContext *ctxt) -: parent(0), engine(0), isInternal(false), publicContext(ctxt), propertyNames(0), contextObject(0), +: parent(0), engine(0), isInternal(false), ownedByParent(false), publicContext(ctxt), propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), optimizedBindings(0), linkedContext(0), componentAttached(0) @@ -515,8 +515,13 @@ QDeclarativeContextData::QDeclarativeContextData(QDeclarativeContext *ctxt) void QDeclarativeContextData::invalidate() { - while (childContexts) - childContexts->invalidate(); + while (childContexts) { + if (childContexts->ownedByParent) { + childContexts->destroy(); + } else { + childContexts->invalidate(); + } + } while (componentAttached) { QDeclarativeComponentAttached *a = componentAttached; @@ -614,7 +619,7 @@ void QDeclarativeContextData::destroy() delete this; } -void QDeclarativeContextData::setParent(QDeclarativeContextData *p) +void QDeclarativeContextData::setParent(QDeclarativeContextData *p, bool parentTakesOwnership) { if (p) { parent = p; @@ -623,6 +628,7 @@ void QDeclarativeContextData::setParent(QDeclarativeContextData *p) if (nextChild) nextChild->prevChild = &nextChild; prevChild = &p->childContexts; p->childContexts = this; + ownedByParent = parentTakesOwnership; } } @@ -662,72 +668,6 @@ void QDeclarativeContextData::addObject(QObject *o) contextObjects = data; } -void QDeclarativeContextData::addImportedScript(const QDeclarativeParser::Object::ScriptBlock &script) -{ - if (!engine) - return; - - QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); - QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); - - const QString &code = script.code; - const QString &url = script.file; - const QDeclarativeParser::Object::ScriptBlock::Pragmas &pragmas = script.pragmas; - - Q_ASSERT(!url.isEmpty()); - - if (pragmas & QDeclarativeParser::Object::ScriptBlock::Shared) { - - QHash<QString, QScriptValue>::Iterator iter = enginePriv->m_sharedScriptImports.find(url); - if (iter == enginePriv->m_sharedScriptImports.end()) { - QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); - - scriptContext->pushScope(enginePriv->contextClass->newUrlContext(url)); - scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject()); - - QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine); - scriptContext->pushScope(scope); - - scriptEngine->evaluate(code, url, 1); - - if (scriptEngine->hasUncaughtException()) { - QDeclarativeError error; - QDeclarativeExpressionPrivate::exceptionToError(scriptEngine, error); - enginePriv->warning(error); - } - - scriptEngine->popContext(); - - iter = enginePriv->m_sharedScriptImports.insert(url, scope); - } - - importedScripts.append(*iter); - - } else { - - QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); - - scriptContext->pushScope(enginePriv->contextClass->newUrlContext(this, 0, url)); - scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject()); - - QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine); - scriptContext->pushScope(scope); - - scriptEngine->evaluate(code, url, 1); - - if (scriptEngine->hasUncaughtException()) { - QDeclarativeError error; - QDeclarativeExpressionPrivate::exceptionToError(scriptEngine, error); - enginePriv->warning(error); - } - - scriptEngine->popContext(); - - importedScripts.append(scope); - - } -} - void QDeclarativeContextData::setIdProperty(int idx, QObject *obj) { idValues[idx] = obj; diff --git a/src/declarative/qml/qdeclarativecontext_p.h b/src/declarative/qml/qdeclarativecontext_p.h index b7e4c6aa7c..29ca091fc2 100644 --- a/src/declarative/qml/qdeclarativecontext_p.h +++ b/src/declarative/qml/qdeclarativecontext_p.h @@ -77,7 +77,7 @@ class QDeclarativeEngine; class QDeclarativeExpression; class QDeclarativeExpressionPrivate; class QDeclarativeAbstractExpression; -class QDeclarativeCompiledBindings; +class QDeclarativeV4Bindings; class QDeclarativeContextData; class QDeclarativeContextPrivate : public QObjectPrivate @@ -124,7 +124,7 @@ public: QDeclarativeContextData *parent; QDeclarativeEngine *engine; - void setParent(QDeclarativeContextData *); + void setParent(QDeclarativeContextData *, bool parentTakesOwnership = false); void refreshExpressions(); void addObject(QObject *); @@ -135,7 +135,9 @@ public: // If internal is false publicContext owns this. QDeclarativeContext *asQDeclarativeContext(); QDeclarativeContextPrivate *asQDeclarativeContextPrivate(); - bool isInternal; + quint32 isInternal:1; + quint32 ownedByParent:1; // unrelated to isInternal; parent context deletes children if true. + quint32 dummy:30; QDeclarativeContext *publicContext; // Property name cache @@ -146,7 +148,6 @@ public: // Any script blocks that exist on this context QList<QScriptValue> importedScripts; - void addImportedScript(const QDeclarativeParser::Object::ScriptBlock &script); // Context base url QUrl url; @@ -188,7 +189,7 @@ public: void setIdPropertyData(QDeclarativeIntegerCache *); // Optimized binding pointer - QDeclarativeCompiledBindings *optimizedBindings; + QDeclarativeV4Bindings *optimizedBindings; // Linked contexts. this owns linkedContext. QDeclarativeContextData *linkedContext; diff --git a/src/declarative/qml/qdeclarativedirparser.cpp b/src/declarative/qml/qdeclarativedirparser.cpp index b5ad33d63e..97f7f4dda8 100644 --- a/src/declarative/qml/qdeclarativedirparser.cpp +++ b/src/declarative/qml/qdeclarativedirparser.cpp @@ -142,7 +142,7 @@ bool QDeclarativeDirParser::parse() } else if (sections[0] == QLatin1String("plugin")) { if (sectionCount < 2) { reportError(lineNumber, -1, - QString::fromUtf8("plugin directive requires 2 arguments, but %1 were provided").arg(sectionCount + 1)); + QString::fromUtf8("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1)); continue; } @@ -154,12 +154,22 @@ bool QDeclarativeDirParser::parse() } else if (sections[0] == QLatin1String("internal")) { if (sectionCount != 3) { reportError(lineNumber, -1, - QString::fromUtf8("internal types require 2 arguments, but %1 were provided").arg(sectionCount + 1)); + QString::fromUtf8("internal types require 2 arguments, but %1 were provided").arg(sectionCount - 1)); continue; } Component entry(sections[1], sections[2], -1, -1); entry.internal = true; _components.append(entry); + } else if (sections[0] == QLatin1String("typeinfo")) { + if (sectionCount != 2) { + reportError(lineNumber, -1, + QString::fromUtf8("typeinfo requires 1 argument, but %1 were provided").arg(sectionCount - 1)); + continue; + } +#ifdef QT_CREATOR + TypeInfo typeInfo(sections[1]); + _typeInfos.append(typeInfo); +#endif } else if (sectionCount == 2) { // No version specified (should only be used for relative qmldir files) @@ -189,7 +199,7 @@ bool QDeclarativeDirParser::parse() } } else { reportError(lineNumber, -1, - QString::fromUtf8("a component declaration requires 3 arguments, but %1 were provided").arg(sectionCount + 1)); + QString::fromUtf8("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount)); } } @@ -229,4 +239,11 @@ QList<QDeclarativeDirParser::Component> QDeclarativeDirParser::components() cons return _components; } +#ifdef QT_CREATOR +QList<TypeInfo> QDeclarativeDirParser::typeInfos() const +{ + return _typeInfos; +} +#endif + QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativedirparser_p.h b/src/declarative/qml/qdeclarativedirparser_p.h index 95f14bc487..d09b90e7a2 100644 --- a/src/declarative/qml/qdeclarativedirparser_p.h +++ b/src/declarative/qml/qdeclarativedirparser_p.h @@ -109,6 +109,19 @@ public: QList<Component> components() const; QList<Plugin> plugins() const; +#ifdef QT_CREATOR + struct TypeInfo + { + TypeInfo() {} + TypeInfo(const QString &fileName) + : fileName(fileName) {} + + QString fileName; + }; + + QList<TypeInfo> typeInfos() const; +#endif + private: void reportError(int line, int column, const QString &message); @@ -118,6 +131,9 @@ private: QString _source; QList<Component> _components; QList<Plugin> _plugins; +#ifdef QT_CREATOR + QList<TypeInfo> _typeInfos; +#endif unsigned _isParsed: 1; }; diff --git a/src/declarative/qml/qdeclarativedom.cpp b/src/declarative/qml/qdeclarativedom.cpp deleted file mode 100644 index f1296aaf18..0000000000 --- a/src/declarative/qml/qdeclarativedom.cpp +++ /dev/null @@ -1,1835 +0,0 @@ -/**************************************************************************** -** -** 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$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, 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. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "private/qdeclarativedom_p.h" -#include "private/qdeclarativedom_p_p.h" - -#include "private/qdeclarativecompiler_p.h" -#include "private/qdeclarativeengine_p.h" -#include "private/qdeclarativescriptparser_p.h" -#include "private/qdeclarativeglobal_p.h" - -#include <QtCore/QByteArray> -#include <QtCore/QDebug> -#include <QtCore/QString> - -QT_BEGIN_NAMESPACE - -QDeclarativeDomDocumentPrivate::QDeclarativeDomDocumentPrivate() -: root(0) -{ -} - -QDeclarativeDomDocumentPrivate::~QDeclarativeDomDocumentPrivate() -{ - if (root) root->release(); -} - -/*! - \class QDeclarativeDomDocument - \internal - \brief The QDeclarativeDomDocument class represents the root of a QML document - - A QML document is a self-contained snippet of QML, usually contained in a - single file. Each document has a root object, accessible through - QDeclarativeDomDocument::rootObject(). - - The QDeclarativeDomDocument class allows the programmer to inspect a QML document by - calling QDeclarativeDomDocument::load(). - - The following example loads a QML file from disk, and prints out its root - object type and the properties assigned in the root object. - \code - QFile file(inputFileName); - file.open(QIODevice::ReadOnly); - QByteArray xmlData = file.readAll(); - - QDeclarativeDomDocument document; - document.load(qmlengine, xmlData); - - QDeclarativeDomObject rootObject = document.rootObject(); - qDebug() << rootObject.objectType(); - foreach(QDeclarativeDomProperty property, rootObject.properties()) - qDebug() << property.propertyName(); - \endcode -*/ - -/*! - Construct an empty QDeclarativeDomDocument. -*/ -QDeclarativeDomDocument::QDeclarativeDomDocument() -: d(new QDeclarativeDomDocumentPrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomDocument. -*/ -QDeclarativeDomDocument::QDeclarativeDomDocument(const QDeclarativeDomDocument &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomDocument -*/ -QDeclarativeDomDocument::~QDeclarativeDomDocument() -{ -} - -/*! - Assign \a other to this QDeclarativeDomDocument. -*/ -QDeclarativeDomDocument &QDeclarativeDomDocument::operator=(const QDeclarativeDomDocument &other) -{ - d = other.d; - return *this; -} - -/*! - Returns all import statements in qml. -*/ -QList<QDeclarativeDomImport> QDeclarativeDomDocument::imports() const -{ - return d->imports; -} - -/*! - Loads a QDeclarativeDomDocument from \a data. \a data should be valid QML - data. On success, true is returned. If the \a data is malformed, false - is returned and QDeclarativeDomDocument::errors() contains an error description. - - \sa QDeclarativeDomDocument::loadError() -*/ -bool QDeclarativeDomDocument::load(QDeclarativeEngine *engine, const QByteArray &data, const QUrl &url) -{ - d->errors.clear(); - d->imports.clear(); - - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - QDeclarativeTypeData *td = ep->typeLoader.get(data, url, QDeclarativeTypeLoader::PreserveParser); - - if(td->isError()) { - d->errors = td->errors(); - td->release(); - return false; - } else if(!td->isCompleteOrError()) { - QDeclarativeError error; - error.setDescription(QLatin1String("QDeclarativeDomDocument supports local types only")); - d->errors << error; - td->release(); - return false; - } - - for (int i = 0; i < td->parser().imports().size(); ++i) { - QDeclarativeScriptParser::Import parserImport = td->parser().imports().at(i); - QDeclarativeDomImport domImport; - domImport.d->type = static_cast<QDeclarativeDomImportPrivate::Type>(parserImport.type); - domImport.d->uri = parserImport.uri; - domImport.d->qualifier = parserImport.qualifier; - domImport.d->version = parserImport.version; - d->imports += domImport; - } - - if (td->parser().tree()) { - d->root = td->parser().tree(); - d->root->addref(); - } - - td->release(); - return true; -} - -/*! - Returns the last load errors. The load errors will be reset after a - successful call to load(). - - \sa load() -*/ -QList<QDeclarativeError> QDeclarativeDomDocument::errors() const -{ - return d->errors; -} - -/*! - Returns the document's root object, or an invalid QDeclarativeDomObject if the - document has no root. - - In the sample QML below, the root object will be the QDeclarativeItem type. - \qml -Item { - Text { - text: "Hello World" - } -} - \endqml -*/ -QDeclarativeDomObject QDeclarativeDomDocument::rootObject() const -{ - QDeclarativeDomObject rv; - rv.d->object = d->root; - if (rv.d->object) rv.d->object->addref(); - return rv; -} - -QDeclarativeDomPropertyPrivate::QDeclarativeDomPropertyPrivate() -: property(0) -{ -} - -QDeclarativeDomPropertyPrivate::~QDeclarativeDomPropertyPrivate() -{ - if (property) property->release(); -} - -QDeclarativeDomDynamicPropertyPrivate::QDeclarativeDomDynamicPropertyPrivate(): - valid(false) -{ -} - -QDeclarativeDomDynamicPropertyPrivate::~QDeclarativeDomDynamicPropertyPrivate() -{ - if (valid && property.defaultValue) property.defaultValue->release(); -} - -/*! - \class QDeclarativeDomProperty - \internal - \brief The QDeclarativeDomProperty class represents one property assignment in the - QML DOM tree - - Properties in QML can be assigned QML \l {QDeclarativeDomValue}{values}. - - \sa QDeclarativeDomObject -*/ - -/*! - Construct an invalid QDeclarativeDomProperty. -*/ -QDeclarativeDomProperty::QDeclarativeDomProperty() -: d(new QDeclarativeDomPropertyPrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomProperty. -*/ -QDeclarativeDomProperty::QDeclarativeDomProperty(const QDeclarativeDomProperty &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomProperty. -*/ -QDeclarativeDomProperty::~QDeclarativeDomProperty() -{ -} - -/*! - Assign \a other to this QDeclarativeDomProperty. -*/ -QDeclarativeDomProperty &QDeclarativeDomProperty::operator=(const QDeclarativeDomProperty &other) -{ - d = other.d; - return *this; -} - -/*! - Returns true if this is a valid QDeclarativeDomProperty, false otherwise. -*/ -bool QDeclarativeDomProperty::isValid() const -{ - return d->property != 0; -} - - -/*! - Return the name of this property. - - \qml -Text { - x: 10 - y: 10 - font.bold: true -} - \endqml - - As illustrated above, a property name can be a simple string, such as "x" or - "y", or a more complex "dot property", such as "font.bold". In both cases - the full name is returned ("x", "y" and "font.bold") by this method. - - For dot properties, a split version of the name can be accessed by calling - QDeclarativeDomProperty::propertyNameParts(). - - \sa QDeclarativeDomProperty::propertyNameParts() -*/ -QByteArray QDeclarativeDomProperty::propertyName() const -{ - return d->propertyName; -} - -/*! - Return the name of this property, split into multiple parts in the case - of dot properties. - - \qml -Text { - x: 10 - y: 10 - font.bold: true -} - \endqml - - For each of the properties shown above, this method would return ("x"), - ("y") and ("font", "bold"). - - \sa QDeclarativeDomProperty::propertyName() -*/ -QList<QByteArray> QDeclarativeDomProperty::propertyNameParts() const -{ - if (d->propertyName.isEmpty()) return QList<QByteArray>(); - else return d->propertyName.split('.'); -} - -/*! - Return true if this property is used as a default property in the QML - document. - - \code -<Text text="hello"/> -<Text>hello</Text> - \endcode - - The above two examples return the same DOM tree, except that the second has - the default property flag set on the text property. Observe that whether - or not a property has isDefaultProperty set is determined by how the - property is used, and not only by whether the property is the types default - property. -*/ -bool QDeclarativeDomProperty::isDefaultProperty() const -{ - return d->property && d->property->isDefault; -} - -/*! - Returns the QDeclarativeDomValue that is assigned to this property, or an invalid - QDeclarativeDomValue if no value is assigned. -*/ -QDeclarativeDomValue QDeclarativeDomProperty::value() const -{ - QDeclarativeDomValue rv; - if (d->property) { - rv.d->property = d->property; - if (d->property->values.count()) - rv.d->value = d->property->values.at(0); - else - rv.d->value = d->property->onValues.at(0); - rv.d->property->addref(); - rv.d->value->addref(); - } - return rv; -} - -/*! - Returns the position in the input data where the property ID startd, or -1 if - the property is invalid. -*/ -int QDeclarativeDomProperty::position() const -{ - if (d && d->property) { - return d->property->location.range.offset; - } else - return -1; -} - -/*! - Returns the length in the input data from where the property ID started upto - the end of it, or -1 if the property is invalid. -*/ -int QDeclarativeDomProperty::length() const -{ - if (d && d->property) - return d->property->location.range.length; - else - return -1; -} - -/*! - Construct an invalid QDeclarativeDomDynamicProperty. -*/ -QDeclarativeDomDynamicProperty::QDeclarativeDomDynamicProperty(): - d(new QDeclarativeDomDynamicPropertyPrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomDynamicProperty. -*/ -QDeclarativeDomDynamicProperty::QDeclarativeDomDynamicProperty(const QDeclarativeDomDynamicProperty &other): - d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomDynamicProperty. -*/ -QDeclarativeDomDynamicProperty::~QDeclarativeDomDynamicProperty() -{ -} - -/*! - Assign \a other to this QDeclarativeDomDynamicProperty. -*/ -QDeclarativeDomDynamicProperty &QDeclarativeDomDynamicProperty::operator=(const QDeclarativeDomDynamicProperty &other) -{ - d = other.d; - return *this; -} - -bool QDeclarativeDomDynamicProperty::isValid() const -{ - return d && d->valid; -} - -/*! - Return the name of this dynamic property. - - \qml -Item { - property int count: 10; -} - \endqml - - As illustrated above, a dynamic property name can have a name and a - default value ("10"). -*/ -QByteArray QDeclarativeDomDynamicProperty::propertyName() const -{ - if (isValid()) - return d->property.name; - else - return QByteArray(); -} - -/*! - Returns the type of the dynamic property. Note that when the property is an - alias property, this will return -1. Use QDeclarativeDomProperty::isAlias() to check - if the property is an alias. -*/ -int QDeclarativeDomDynamicProperty::propertyType() const -{ - if (isValid()) { - switch (d->property.type) { - case QDeclarativeParser::Object::DynamicProperty::Bool: - return QMetaType::type("bool"); - - case QDeclarativeParser::Object::DynamicProperty::Color: - return QMetaType::type("QColor"); - - case QDeclarativeParser::Object::DynamicProperty::Time: - return QMetaType::type("QTime"); - - case QDeclarativeParser::Object::DynamicProperty::Date: - return QMetaType::type("QDate"); - - case QDeclarativeParser::Object::DynamicProperty::DateTime: - return QMetaType::type("QDateTime"); - - case QDeclarativeParser::Object::DynamicProperty::Int: - return QMetaType::type("int"); - - case QDeclarativeParser::Object::DynamicProperty::Real: - return sizeof(qreal) == sizeof(double) ? QMetaType::type("double") : QMetaType::type("float"); - - case QDeclarativeParser::Object::DynamicProperty::String: - return QMetaType::type("QString"); - - case QDeclarativeParser::Object::DynamicProperty::Url: - return QMetaType::type("QUrl"); - - case QDeclarativeParser::Object::DynamicProperty::Variant: - return QMetaType::type("QVariant"); - - default: - break; - } - } - - return -1; -} - -QByteArray QDeclarativeDomDynamicProperty::propertyTypeName() const -{ - if (isValid()) - return d->property.customType; - - return QByteArray(); -} - -/*! - Return true if this property is used as a default property in the QML - document. - - \code -<Text text="hello"/> -<Text>hello</Text> - \endcode - - The above two examples return the same DOM tree, except that the second has - the default property flag set on the text property. Observe that whether - or not a property has isDefaultProperty set is determined by how the - property is used, and not only by whether the property is the types default - property. -*/ -bool QDeclarativeDomDynamicProperty::isDefaultProperty() const -{ - if (isValid()) - return d->property.isDefaultProperty; - else - return false; -} - -/*! - Returns the default value as a QDeclarativeDomProperty. -*/ -QDeclarativeDomProperty QDeclarativeDomDynamicProperty::defaultValue() const -{ - QDeclarativeDomProperty rp; - - if (isValid() && d->property.defaultValue) { - rp.d->property = d->property.defaultValue; - rp.d->propertyName = propertyName(); - rp.d->property->addref(); - } - - return rp; -} - -/*! - Returns true if this dynamic property is an alias for another property, - false otherwise. -*/ -bool QDeclarativeDomDynamicProperty::isAlias() const -{ - if (isValid()) - return d->property.type == QDeclarativeParser::Object::DynamicProperty::Alias; - else - return false; -} - -/*! - Returns the position in the input data where the property ID startd, or 0 if - the property is invalid. -*/ -int QDeclarativeDomDynamicProperty::position() const -{ - if (isValid()) { - return d->property.location.range.offset; - } else - return -1; -} - -/*! - Returns the length in the input data from where the property ID started upto - the end of it, or 0 if the property is invalid. -*/ -int QDeclarativeDomDynamicProperty::length() const -{ - if (isValid()) - return d->property.location.range.length; - else - return -1; -} - -QDeclarativeDomObjectPrivate::QDeclarativeDomObjectPrivate() -: object(0) -{ -} - -QDeclarativeDomObjectPrivate::~QDeclarativeDomObjectPrivate() -{ - if (object) object->release(); -} - -QDeclarativeDomObjectPrivate::Properties -QDeclarativeDomObjectPrivate::properties() const -{ - Properties rv; - - for (QHash<QByteArray, QDeclarativeParser::Property *>::ConstIterator iter = - object->properties.begin(); - iter != object->properties.end(); - ++iter) { - - rv << properties(*iter); - - } - return rv; -} - -QDeclarativeDomObjectPrivate::Properties -QDeclarativeDomObjectPrivate::properties(QDeclarativeParser::Property *property) const -{ - Properties rv; - - if (property->value) { - - for (QHash<QByteArray, QDeclarativeParser::Property *>::ConstIterator iter = - property->value->properties.begin(); - iter != property->value->properties.end(); - ++iter) { - - rv << properties(*iter); - - } - - QByteArray name(property->name + '.'); - for (Properties::Iterator iter = rv.begin(); iter != rv.end(); ++iter) - iter->second.prepend(name); - - } else { - rv << qMakePair(property, property->name); - } - - return rv; -} - -/*! - \class QDeclarativeDomObject - \internal - \brief The QDeclarativeDomObject class represents an object instantiation. - - Each object instantiated in a QML file has a corresponding QDeclarativeDomObject - node in the QML DOM. - - In addition to the type information that determines the object to - instantiate, QDeclarativeDomObject's also have a set of associated QDeclarativeDomProperty's. - Each QDeclarativeDomProperty represents a QML property assignment on the instantiated - object. For example, - - \qml -QGraphicsWidget { - opacity: 0.5 - size: "100x100" -} - \endqml - - describes a single QDeclarativeDomObject - "QGraphicsWidget" - with two properties, - "opacity" and "size". Obviously QGraphicsWidget has many more properties than just - these two, but the QML DOM representation only contains those assigned - values (or bindings) in the QML file. -*/ - -/*! - Construct an invalid QDeclarativeDomObject. -*/ -QDeclarativeDomObject::QDeclarativeDomObject() -: d(new QDeclarativeDomObjectPrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomObject. -*/ -QDeclarativeDomObject::QDeclarativeDomObject(const QDeclarativeDomObject &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomObject. -*/ -QDeclarativeDomObject::~QDeclarativeDomObject() -{ -} - -/*! - Assign \a other to this QDeclarativeDomObject. -*/ -QDeclarativeDomObject &QDeclarativeDomObject::operator=(const QDeclarativeDomObject &other) -{ - d = other.d; - return *this; -} - -/*! - Returns true if this is a valid QDeclarativeDomObject, false otherwise. -*/ -bool QDeclarativeDomObject::isValid() const -{ - return d->object != 0; -} - -/*! - Returns the fully-qualified type name of this object. - - For example, the type of this object would be "Qt/4.6/Rectangle". - \qml -Rectangle { } - \endqml -*/ -QByteArray QDeclarativeDomObject::objectType() const -{ - if (d->object) return d->object->typeName; - else return QByteArray(); -} - -/*! - Returns the type name as referenced in the qml file. - - For example, the type of this object would be "Rectangle". - \qml -Rectangle { } - \endqml -*/ -QByteArray QDeclarativeDomObject::objectClassName() const -{ - if (d->object) - return d->object->className; - else - return QByteArray(); -} - -int QDeclarativeDomObject::objectTypeMajorVersion() const -{ - if (d->object) - return d->object->majorVersion; - else - return -1; -} - -int QDeclarativeDomObject::objectTypeMinorVersion() const -{ - if (d->object) - return d->object->minorVersion; - else - return -1; -} - -/*! - Returns the QML id assigned to this object, or an empty QByteArray if no id - has been assigned. - - For example, the object id of this object would be "MyText". - \qml -Text { id: myText } - \endqml -*/ -QString QDeclarativeDomObject::objectId() const -{ - if (d->object) { - return d->object->id; - } else { - return QString(); - } -} - -/*! - Returns the list of assigned properties on this object. - - In the following example, "text" and "x" properties would be returned. - \qml -Text { - text: "Hello world!" - x: 100 -} - \endqml -*/ -QList<QDeclarativeDomProperty> QDeclarativeDomObject::properties() const -{ - QList<QDeclarativeDomProperty> rv; - - if (!d->object || isComponent()) - return rv; - - QDeclarativeDomObjectPrivate::Properties properties = d->properties(); - for (int ii = 0; ii < properties.count(); ++ii) { - - QDeclarativeDomProperty domProperty; - domProperty.d->property = properties.at(ii).first; - domProperty.d->property->addref(); - domProperty.d->propertyName = properties.at(ii).second; - rv << domProperty; - - } - - if (d->object->defaultProperty) { - QDeclarativeDomProperty domProperty; - domProperty.d->property = d->object->defaultProperty; - domProperty.d->property->addref(); - domProperty.d->propertyName = d->object->defaultProperty->name; - rv << domProperty; - } - - return rv; -} - -/*! - Returns the object's \a name property if a value has been assigned to - it, or an invalid QDeclarativeDomProperty otherwise. - - In the example below, \c {object.property("source")} would return a valid - QDeclarativeDomProperty, and \c {object.property("tile")} an invalid QDeclarativeDomProperty. - - \qml -Image { source: "sample.jpg" } - \endqml -*/ -QDeclarativeDomProperty QDeclarativeDomObject::property(const QByteArray &name) const -{ - QList<QDeclarativeDomProperty> props = properties(); - for (int ii = 0; ii < props.count(); ++ii) - if (props.at(ii).propertyName() == name) - return props.at(ii); - return QDeclarativeDomProperty(); -} - -QList<QDeclarativeDomDynamicProperty> QDeclarativeDomObject::dynamicProperties() const -{ - QList<QDeclarativeDomDynamicProperty> properties; - - for (int i = 0; i < d->object->dynamicProperties.size(); ++i) { - QDeclarativeDomDynamicProperty p; - p.d = new QDeclarativeDomDynamicPropertyPrivate; - p.d->property = d->object->dynamicProperties.at(i); - p.d->valid = true; - - if (p.d->property.defaultValue) - p.d->property.defaultValue->addref(); - - properties.append(p); - } - - return properties; -} - -QDeclarativeDomDynamicProperty QDeclarativeDomObject::dynamicProperty(const QByteArray &name) const -{ - QDeclarativeDomDynamicProperty p; - - if (!isValid()) - return p; - - for (int i = 0; i < d->object->dynamicProperties.size(); ++i) { - if (d->object->dynamicProperties.at(i).name == name) { - p.d = new QDeclarativeDomDynamicPropertyPrivate; - p.d->property = d->object->dynamicProperties.at(i); - if (p.d->property.defaultValue) p.d->property.defaultValue->addref(); - p.d->valid = true; - } - } - - return p; -} - -/*! - Returns true if this object is a custom type. Custom types are special - types that allow embeddeding non-QML data, such as SVG or HTML data, - directly into QML files. - - \note Currently this method will always return false, and is a placekeeper - for future functionality. - - \sa QDeclarativeDomObject::customTypeData() -*/ -bool QDeclarativeDomObject::isCustomType() const -{ - return false; -} - -/*! - If this object represents a custom type, returns the data associated with - the custom type, otherwise returns an empty QByteArray(). - QDeclarativeDomObject::isCustomType() can be used to check if this object represents - a custom type. -*/ -QByteArray QDeclarativeDomObject::customTypeData() const -{ - return QByteArray(); -} - -/*! - Returns true if this object is a sub-component object. Sub-component - objects can be converted into QDeclarativeDomComponent instances by calling - QDeclarativeDomObject::toComponent(). - - \sa QDeclarativeDomObject::toComponent() -*/ -bool QDeclarativeDomObject::isComponent() const -{ - return (d->object && (d->object->typeName == "Qt/Component" || d->object->typeName == "QtQuick/Component")); -} - -/*! - Returns a QDeclarativeDomComponent for this object if it is a sub-component, or - an invalid QDeclarativeDomComponent if not. QDeclarativeDomObject::isComponent() can be used - to check if this object represents a sub-component. - - \sa QDeclarativeDomObject::isComponent() -*/ -QDeclarativeDomComponent QDeclarativeDomObject::toComponent() const -{ - QDeclarativeDomComponent rv; - if (isComponent()) - rv.d = d; - return rv; -} - -/*! - Returns the position in the input data where the property assignment started -, or -1 if the property is invalid. -*/ -int QDeclarativeDomObject::position() const -{ - if (d && d->object) - return d->object->location.range.offset; - else - return -1; -} - -/*! - Returns the length in the input data from where the property assignment star -ted upto the end of it, or -1 if the property is invalid. -*/ -int QDeclarativeDomObject::length() const -{ - if (d && d->object) - return d->object->location.range.length; - else - return -1; -} - -// Returns the URL of the type, if it is an external type, or an empty URL if -// not -QUrl QDeclarativeDomObject::url() const -{ - if (d && d->object) - return d->object->url; - else - return QUrl(); -} - -QDeclarativeDomBasicValuePrivate::QDeclarativeDomBasicValuePrivate() -: value(0) -{ -} - -QDeclarativeDomBasicValuePrivate::~QDeclarativeDomBasicValuePrivate() -{ - if (value) value->release(); -} - -/*! - \class QDeclarativeDomValueLiteral - \internal - \brief The QDeclarativeDomValueLiteral class represents a literal value. - - A literal value is a simple value, written inline with the QML. In the - example below, the "x", "y" and "color" properties are being assigned - literal values. - - \qml -Rectangle { - x: 10 - y: 10 - color: "red" -} - \endqml -*/ - -/*! - Construct an empty QDeclarativeDomValueLiteral. -*/ -QDeclarativeDomValueLiteral::QDeclarativeDomValueLiteral(): - d(new QDeclarativeDomBasicValuePrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomValueLiteral. -*/ -QDeclarativeDomValueLiteral::QDeclarativeDomValueLiteral(const QDeclarativeDomValueLiteral &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomValueLiteral. -*/ -QDeclarativeDomValueLiteral::~QDeclarativeDomValueLiteral() -{ -} - -/*! - Assign \a other to this QDeclarativeDomValueLiteral. -*/ -QDeclarativeDomValueLiteral &QDeclarativeDomValueLiteral::operator=(const QDeclarativeDomValueLiteral &other) -{ - d = other.d; - return *this; -} - -/*! - Return the literal value. - - In the example below, the literal value will be the string "10". - \qml -Rectangle { x: 10 } - \endqml -*/ -QString QDeclarativeDomValueLiteral::literal() const -{ - if (d->value) return d->value->primitive(); - else return QString(); -} - -/*! - \class QDeclarativeDomValueBinding - \internal - \brief The QDeclarativeDomValueBinding class represents a property binding. - - A property binding is an ECMAScript expression assigned to a property. In - the example below, the "x" property is being assigned a property binding. - - \qml -Rectangle { x: Other.x } - \endqml -*/ - -/*! - Construct an empty QDeclarativeDomValueBinding. -*/ -QDeclarativeDomValueBinding::QDeclarativeDomValueBinding(): - d(new QDeclarativeDomBasicValuePrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomValueBinding. -*/ -QDeclarativeDomValueBinding::QDeclarativeDomValueBinding(const QDeclarativeDomValueBinding &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomValueBinding. -*/ -QDeclarativeDomValueBinding::~QDeclarativeDomValueBinding() -{ -} - -/*! - Assign \a other to this QDeclarativeDomValueBinding. -*/ -QDeclarativeDomValueBinding &QDeclarativeDomValueBinding::operator=(const QDeclarativeDomValueBinding &other) -{ - d = other.d; - return *this; -} - -/*! - Return the binding expression. - - In the example below, the string "Other.x" will be returned. - \qml -Rectangle { x: Other.x } - \endqml -*/ -QString QDeclarativeDomValueBinding::binding() const -{ - if (d->value) - return d->value->value.asScript(); - else - return QString(); -} - -/*! - \class QDeclarativeDomValueValueSource - \internal - \brief The QDeclarativeDomValueValueSource class represents a value source assignment value. - - In QML, value sources are special value generating types that may be - assigned to properties. Value sources inherit the QDeclarativePropertyValueSource - class. In the example below, the "x" property is being assigned the - NumberAnimation value source. - - \qml -Rectangle { - x: NumberAnimation { - from: 0 - to: 100 - loops: Animation.Infinite - } -} - \endqml -*/ - -/*! - Construct an empty QDeclarativeDomValueValueSource. -*/ -QDeclarativeDomValueValueSource::QDeclarativeDomValueValueSource(): - d(new QDeclarativeDomBasicValuePrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomValueValueSource. -*/ -QDeclarativeDomValueValueSource::QDeclarativeDomValueValueSource(const QDeclarativeDomValueValueSource &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomValueValueSource. -*/ -QDeclarativeDomValueValueSource::~QDeclarativeDomValueValueSource() -{ -} - -/*! - Assign \a other to this QDeclarativeDomValueValueSource. -*/ -QDeclarativeDomValueValueSource &QDeclarativeDomValueValueSource::operator=(const QDeclarativeDomValueValueSource &other) -{ - d = other.d; - return *this; -} - -/*! - Return the value source object. - - In the example below, an object representing the NumberAnimation will be - returned. - \qml -Rectangle { - x: NumberAnimation { - from: 0 - to: 100 - loops: Animation.Infinite - } -} - \endqml -*/ -QDeclarativeDomObject QDeclarativeDomValueValueSource::object() const -{ - QDeclarativeDomObject rv; - if (d->value) { - rv.d->object = d->value->object; - rv.d->object->addref(); - } - return rv; -} - -/*! - \class QDeclarativeDomValueValueInterceptor - \internal - \brief The QDeclarativeDomValueValueInterceptor class represents a value interceptor assignment value. - - In QML, value interceptor are special write-intercepting types that may be - assigned to properties. Value interceptor inherit the QDeclarativePropertyValueInterceptor - class. In the example below, the "x" property is being assigned the - Behavior value interceptor. - - \qml -Rectangle { - Behavior on x { NumberAnimation { duration: 500 } } -} - \endqml -*/ - -/*! - Construct an empty QDeclarativeDomValueValueInterceptor. -*/ -QDeclarativeDomValueValueInterceptor::QDeclarativeDomValueValueInterceptor(): - d(new QDeclarativeDomBasicValuePrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomValueValueInterceptor. -*/ -QDeclarativeDomValueValueInterceptor::QDeclarativeDomValueValueInterceptor(const QDeclarativeDomValueValueInterceptor &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomValueValueInterceptor. -*/ -QDeclarativeDomValueValueInterceptor::~QDeclarativeDomValueValueInterceptor() -{ -} - -/*! - Assign \a other to this QDeclarativeDomValueValueInterceptor. -*/ -QDeclarativeDomValueValueInterceptor &QDeclarativeDomValueValueInterceptor::operator=(const QDeclarativeDomValueValueInterceptor &other) -{ - d = other.d; - return *this; -} - -/*! - Return the value interceptor object. - - In the example below, an object representing the Behavior will be - returned. - \qml -Rectangle { - Behavior on x { NumberAnimation { duration: 500 } } -} - \endqml -*/ -QDeclarativeDomObject QDeclarativeDomValueValueInterceptor::object() const -{ - QDeclarativeDomObject rv; - if (d->value) { - rv.d->object = d->value->object; - rv.d->object->addref(); - } - return rv; -} - -QDeclarativeDomValuePrivate::QDeclarativeDomValuePrivate() -: property(0), value(0) -{ -} - -QDeclarativeDomValuePrivate::~QDeclarativeDomValuePrivate() -{ - if (property) property->release(); - if (value) value->release(); -} - -/*! - \class QDeclarativeDomValue - \internal - \brief The QDeclarativeDomValue class represents a generic Qml value. - - QDeclarativeDomValue's can be assigned to QML \l {QDeclarativeDomProperty}{properties}. In - QML, properties can be assigned various different values, including basic - literals, property bindings, property value sources, objects and lists of - values. The QDeclarativeDomValue class allows a programmer to determine the specific - value type being assigned and access more detailed information through a - corresponding value type class. - - For example, in the following example, - - \qml -Text { - text: "Hello World!" - y: Other.y -} - \endqml - - The text property is being assigned a literal, and the y property a property - binding. To output the values assigned to the text and y properties in the - above example from C++, - - \code - QDeclarativeDomDocument document; - QDeclarativeDomObject root = document.rootObject(); - - QDeclarativeDomProperty text = root.property("text"); - if (text.value().isLiteral()) { - QDeclarativeDomValueLiteral literal = text.value().toLiteral(); - qDebug() << literal.literal(); - } - - QDeclarativeDomProperty y = root.property("y"); - if (y.value().isBinding()) { - QDeclarativeDomValueBinding binding = y.value().toBinding(); - qDebug() << binding.binding(); - } - \endcode -*/ - -/*! - Construct an invalid QDeclarativeDomValue. -*/ -QDeclarativeDomValue::QDeclarativeDomValue() -: d(new QDeclarativeDomValuePrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomValue. -*/ -QDeclarativeDomValue::QDeclarativeDomValue(const QDeclarativeDomValue &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomValue -*/ -QDeclarativeDomValue::~QDeclarativeDomValue() -{ -} - -/*! - Assign \a other to this QDeclarativeDomValue. -*/ -QDeclarativeDomValue &QDeclarativeDomValue::operator=(const QDeclarativeDomValue &other) -{ - d = other.d; - return *this; -} - -/*! - \enum QDeclarativeDomValue::Type - - The type of the QDeclarativeDomValue node. - - \value Invalid The QDeclarativeDomValue is invalid. - \value Literal The QDeclarativeDomValue is a literal value assignment. Use QDeclarativeDomValue::toLiteral() to access the type instance. - \value PropertyBinding The QDeclarativeDomValue is a property binding. Use QDeclarativeDomValue::toBinding() to access the type instance. - \value ValueSource The QDeclarativeDomValue is a property value source. Use QDeclarativeDomValue::toValueSource() to access the type instance. - \value ValueInterceptor The QDeclarativeDomValue is a property value interceptor. Use QDeclarativeDomValue::toValueInterceptor() to access the type instance. - \value Object The QDeclarativeDomValue is an object assignment. Use QDeclarativeDomValue::toObject() to access the type instnace. - \value List The QDeclarativeDomValue is a list of other values. Use QDeclarativeDomValue::toList() to access the type instance. -*/ - -/*! - Returns the type of this QDeclarativeDomValue. -*/ -QDeclarativeDomValue::Type QDeclarativeDomValue::type() const -{ - if (d->property) - if (QDeclarativeMetaType::isList(d->property->type) || - (d->property && (d->property->values.count() + d->property->onValues.count()) > 1)) - return List; - - QDeclarativeParser::Value *value = d->value; - if (!value && !d->property) - return Invalid; - - switch(value->type) { - case QDeclarativeParser::Value::Unknown: - return Invalid; - case QDeclarativeParser::Value::Literal: - return Literal; - case QDeclarativeParser::Value::PropertyBinding: - return PropertyBinding; - case QDeclarativeParser::Value::ValueSource: - return ValueSource; - case QDeclarativeParser::Value::ValueInterceptor: - return ValueInterceptor; - case QDeclarativeParser::Value::CreatedObject: - return Object; - case QDeclarativeParser::Value::SignalObject: - return Invalid; - case QDeclarativeParser::Value::SignalExpression: - return Literal; - case QDeclarativeParser::Value::Id: - return Literal; - } - return Invalid; -} - -/*! - Returns true if this is an invalid value, otherwise false. -*/ -bool QDeclarativeDomValue::isInvalid() const -{ - return type() == Invalid; -} - -/*! - Returns true if this is a literal value, otherwise false. -*/ -bool QDeclarativeDomValue::isLiteral() const -{ - return type() == Literal; -} - -/*! - Returns true if this is a property binding value, otherwise false. -*/ -bool QDeclarativeDomValue::isBinding() const -{ - return type() == PropertyBinding; -} - -/*! - Returns true if this is a value source value, otherwise false. -*/ -bool QDeclarativeDomValue::isValueSource() const -{ - return type() == ValueSource; -} - -/*! - Returns true if this is a value interceptor value, otherwise false. -*/ -bool QDeclarativeDomValue::isValueInterceptor() const -{ - return type() == ValueInterceptor; -} - -/*! - Returns true if this is an object value, otherwise false. -*/ -bool QDeclarativeDomValue::isObject() const -{ - return type() == Object; -} - -/*! - Returns true if this is a list value, otherwise false. -*/ -bool QDeclarativeDomValue::isList() const -{ - return type() == List; -} - -/*! - Returns a QDeclarativeDomValueLiteral if this value is a literal type, otherwise - returns an invalid QDeclarativeDomValueLiteral. - - \sa QDeclarativeDomValue::type() -*/ -QDeclarativeDomValueLiteral QDeclarativeDomValue::toLiteral() const -{ - QDeclarativeDomValueLiteral rv; - if (type() == Literal) { - rv.d->value = d->value; - rv.d->value->addref(); - } - return rv; -} - -/*! - Returns a QDeclarativeDomValueBinding if this value is a property binding type, - otherwise returns an invalid QDeclarativeDomValueBinding. - - \sa QDeclarativeDomValue::type() -*/ -QDeclarativeDomValueBinding QDeclarativeDomValue::toBinding() const -{ - QDeclarativeDomValueBinding rv; - if (type() == PropertyBinding) { - rv.d->value = d->value; - rv.d->value->addref(); - } - return rv; -} - -/*! - Returns a QDeclarativeDomValueValueSource if this value is a property value source - type, otherwise returns an invalid QDeclarativeDomValueValueSource. - - \sa QDeclarativeDomValue::type() -*/ -QDeclarativeDomValueValueSource QDeclarativeDomValue::toValueSource() const -{ - QDeclarativeDomValueValueSource rv; - if (type() == ValueSource) { - rv.d->value = d->value; - rv.d->value->addref(); - } - return rv; -} - -/*! - Returns a QDeclarativeDomValueValueInterceptor if this value is a property value interceptor - type, otherwise returns an invalid QDeclarativeDomValueValueInterceptor. - - \sa QDeclarativeDomValue::type() -*/ -QDeclarativeDomValueValueInterceptor QDeclarativeDomValue::toValueInterceptor() const -{ - QDeclarativeDomValueValueInterceptor rv; - if (type() == ValueInterceptor) { - rv.d->value = d->value; - rv.d->value->addref(); - } - return rv; -} - -/*! - Returns a QDeclarativeDomObject if this value is an object assignment type, otherwise - returns an invalid QDeclarativeDomObject. - - \sa QDeclarativeDomValue::type() -*/ -QDeclarativeDomObject QDeclarativeDomValue::toObject() const -{ - QDeclarativeDomObject rv; - if (type() == Object) { - rv.d->object = d->value->object; - rv.d->object->addref(); - } - return rv; -} - -/*! - Returns a QDeclarativeDomList if this value is a list type, otherwise returns an - invalid QDeclarativeDomList. - - \sa QDeclarativeDomValue::type() -*/ -QDeclarativeDomList QDeclarativeDomValue::toList() const -{ - QDeclarativeDomList rv; - if (type() == List) { - rv.d = d; - } - return rv; -} - -/*! - Returns the position in the input data where the property value startd, or -1 - if the value is invalid. -*/ -int QDeclarativeDomValue::position() const -{ - if (type() == Invalid) - return -1; - else - return d->value->location.range.offset; -} - -/*! - Returns the length in the input data from where the property value started u -pto the end of it, or -1 if the value is invalid. -*/ -int QDeclarativeDomValue::length() const -{ - if (type() == Invalid) - return -1; - else - return d->value->location.range.length; -} - -/*! - \class QDeclarativeDomList - \internal - \brief The QDeclarativeDomList class represents a list of values assigned to a QML property. - - Lists of values can be assigned to properties. For example, the following - example assigns multiple objects to Item's "children" property - \qml -Item { - children: [ - Text { }, - Rectangle { } - ] -} - \endqml - - Lists can also be implicitly created by assigning multiple - \l {QDeclarativeDomValueValueSource}{value sources} or constants to a property. - \qml -Item { - x: 10 - x: NumberAnimation { - running: false - from: 0 - to: 100 - } -} - \endqml -*/ - -/*! - Construct an empty QDeclarativeDomList. -*/ -QDeclarativeDomList::QDeclarativeDomList() -{ -} - -/*! - Create a copy of \a other QDeclarativeDomList. -*/ -QDeclarativeDomList::QDeclarativeDomList(const QDeclarativeDomList &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomList. -*/ -QDeclarativeDomList::~QDeclarativeDomList() -{ -} - -/*! - Assign \a other to this QDeclarativeDomList. -*/ -QDeclarativeDomList &QDeclarativeDomList::operator=(const QDeclarativeDomList &other) -{ - d = other.d; - return *this; -} - -/*! - Returns the list of QDeclarativeDomValue's. -*/ -QList<QDeclarativeDomValue> QDeclarativeDomList::values() const -{ - QList<QDeclarativeDomValue> rv; - if (!d->property) - return rv; - - for (int ii = 0; ii < d->property->values.count(); ++ii) { - QDeclarativeDomValue v; - v.d->value = d->property->values.at(ii); - v.d->value->addref(); - rv << v; - } - - for (int ii = 0; ii < d->property->onValues.count(); ++ii) { - QDeclarativeDomValue v; - v.d->value = d->property->onValues.at(ii); - v.d->value->addref(); - rv << v; - } - - return rv; -} - -/*! - Returns the position in the input data where the list started, or -1 if - the property is invalid. -*/ -int QDeclarativeDomList::position() const -{ - if (d && d->property) { - return d->property->listValueRange.offset; - } else - return -1; -} - -/*! - Returns the length in the input data from where the list started upto - the end of it, or 0 if the property is invalid. -*/ -int QDeclarativeDomList::length() const -{ - if (d && d->property) - return d->property->listValueRange.length; - else - return -1; -} - -/*! - Returns a list of positions of the commas in the QML file. -*/ -QList<int> QDeclarativeDomList:: commaPositions() const -{ - if (d && d->property) - return d->property->listCommaPositions; - else - return QList<int>(); -} - -/*! - \class QDeclarativeDomComponent - \internal - \brief The QDeclarativeDomComponent class represents sub-component within a QML document. - - Sub-components are QDeclarativeComponents defined within a QML document. The - following example shows the definition of a sub-component with the id - "listDelegate". - - \qml -Item { - Component { - id: listDelegate - Text { - text: modelData.text - } - } -} - \endqml - - Like QDeclarativeDomDocument's, components contain a single root object. -*/ - -/*! - Construct an empty QDeclarativeDomComponent. -*/ -QDeclarativeDomComponent::QDeclarativeDomComponent() -{ -} - -/*! - Create a copy of \a other QDeclarativeDomComponent. -*/ -QDeclarativeDomComponent::QDeclarativeDomComponent(const QDeclarativeDomComponent &other) -: QDeclarativeDomObject(other) -{ -} - -/*! - Destroy the QDeclarativeDomComponent. -*/ -QDeclarativeDomComponent::~QDeclarativeDomComponent() -{ -} - -/*! - Assign \a other to this QDeclarativeDomComponent. -*/ -QDeclarativeDomComponent &QDeclarativeDomComponent::operator=(const QDeclarativeDomComponent &other) -{ - static_cast<QDeclarativeDomObject &>(*this) = other; - return *this; -} - -/*! - Returns the component's root object. - - In the example below, the root object is the "Text" object. - \qml -Item { - Component { - id: listDelegate - Text { - text: modelData.text - } - } -} - \endqml -*/ -QDeclarativeDomObject QDeclarativeDomComponent::componentRoot() const -{ - QDeclarativeDomObject rv; - if (d->object) { - QDeclarativeParser::Object *obj = 0; - if (d->object->defaultProperty && - d->object->defaultProperty->values.count() == 1 && - d->object->defaultProperty->values.at(0)->object) - obj = d->object->defaultProperty->values.at(0)->object; - - if (obj) { - rv.d->object = obj; - rv.d->object->addref(); - } - } - - return rv; -} - -QDeclarativeDomImportPrivate::QDeclarativeDomImportPrivate() -: type(File) -{ -} - -QDeclarativeDomImportPrivate::~QDeclarativeDomImportPrivate() -{ -} - -/*! - \class QDeclarativeDomImport - \internal - \brief The QDeclarativeDomImport class represents an import statement. -*/ - -/*! - Construct an empty QDeclarativeDomImport. -*/ -QDeclarativeDomImport::QDeclarativeDomImport() -: d(new QDeclarativeDomImportPrivate) -{ -} - -/*! - Create a copy of \a other QDeclarativeDomImport. -*/ -QDeclarativeDomImport::QDeclarativeDomImport(const QDeclarativeDomImport &other) -: d(other.d) -{ -} - -/*! - Destroy the QDeclarativeDomImport. -*/ -QDeclarativeDomImport::~QDeclarativeDomImport() -{ -} - -/*! - Assign \a other to this QDeclarativeDomImport. -*/ -QDeclarativeDomImport &QDeclarativeDomImport::operator=(const QDeclarativeDomImport &other) -{ - d = other.d; - return *this; -} - -/*! - Returns the type of the import. - */ -QDeclarativeDomImport::Type QDeclarativeDomImport::type() const -{ - return static_cast<QDeclarativeDomImport::Type>(d->type); -} - -/*! - Returns the URI of the import (e.g. 'subdir' or 'com.nokia.Qt') - */ -QString QDeclarativeDomImport::uri() const -{ - return d->uri; -} - -/*! - Returns the version specified by the import. An empty string if no version was specified. - */ -QString QDeclarativeDomImport::version() const -{ - return d->version; -} - -/*! - Returns the (optional) qualifier string (the token following the 'as' keyword) of the import. - */ -QString QDeclarativeDomImport::qualifier() const -{ - return d->qualifier; -} - -QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativedom_p.h b/src/declarative/qml/qdeclarativedom_p.h deleted file mode 100644 index 64300d47e7..0000000000 --- a/src/declarative/qml/qdeclarativedom_p.h +++ /dev/null @@ -1,362 +0,0 @@ -/**************************************************************************** -** -** 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$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, 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. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QDECLARATIVEDOM_P_H -#define QDECLARATIVEDOM_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qdeclarativeerror.h" - -#include <QtCore/qlist.h> -#include <QtCore/qshareddata.h> - -#include <private/qdeclarativeglobal_p.h> - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QString; -class QByteArray; -class QDeclarativeDomObject; -class QDeclarativeDomList; -class QDeclarativeDomValue; -class QDeclarativeEngine; -class QDeclarativeDomComponent; -class QDeclarativeDomImport; -class QIODevice; - -class QDeclarativeDomDocumentPrivate; - -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomDocument -{ -public: - QDeclarativeDomDocument(); - QDeclarativeDomDocument(const QDeclarativeDomDocument &); - ~QDeclarativeDomDocument(); - QDeclarativeDomDocument &operator=(const QDeclarativeDomDocument &); - - QList<QDeclarativeDomImport> imports() const; - - QList<QDeclarativeError> errors() const; - bool load(QDeclarativeEngine *, const QByteArray &, const QUrl & = QUrl()); - - QDeclarativeDomObject rootObject() const; - -private: - QSharedDataPointer<QDeclarativeDomDocumentPrivate> d; -}; - -class QDeclarativeDomPropertyPrivate; -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomProperty -{ -public: - QDeclarativeDomProperty(); - QDeclarativeDomProperty(const QDeclarativeDomProperty &); - ~QDeclarativeDomProperty(); - QDeclarativeDomProperty &operator=(const QDeclarativeDomProperty &); - - bool isValid() const; - - QByteArray propertyName() const; - QList<QByteArray> propertyNameParts() const; - - bool isDefaultProperty() const; - - QDeclarativeDomValue value() const; - - int position() const; - int length() const; - -private: - friend class QDeclarativeDomObject; - friend class QDeclarativeDomDynamicProperty; - QSharedDataPointer<QDeclarativeDomPropertyPrivate> d; -}; - -class QDeclarativeDomDynamicPropertyPrivate; -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomDynamicProperty -{ -public: - QDeclarativeDomDynamicProperty(); - QDeclarativeDomDynamicProperty(const QDeclarativeDomDynamicProperty &); - ~QDeclarativeDomDynamicProperty(); - QDeclarativeDomDynamicProperty &operator=(const QDeclarativeDomDynamicProperty &); - - bool isValid() const; - - QByteArray propertyName() const; - int propertyType() const; - QByteArray propertyTypeName() const; - - bool isDefaultProperty() const; - QDeclarativeDomProperty defaultValue() const; - - bool isAlias() const; - - int position() const; - int length() const; - -private: - friend class QDeclarativeDomObject; - QSharedDataPointer<QDeclarativeDomDynamicPropertyPrivate> d; -}; - -class QDeclarativeDomObjectPrivate; -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomObject -{ -public: - QDeclarativeDomObject(); - QDeclarativeDomObject(const QDeclarativeDomObject &); - ~QDeclarativeDomObject(); - QDeclarativeDomObject &operator=(const QDeclarativeDomObject &); - - bool isValid() const; - - QByteArray objectType() const; - QByteArray objectClassName() const; - - int objectTypeMajorVersion() const; - int objectTypeMinorVersion() const; - - QString objectId() const; - - QList<QDeclarativeDomProperty> properties() const; - QDeclarativeDomProperty property(const QByteArray &) const; - - QList<QDeclarativeDomDynamicProperty> dynamicProperties() const; - QDeclarativeDomDynamicProperty dynamicProperty(const QByteArray &) const; - - bool isCustomType() const; - QByteArray customTypeData() const; - - bool isComponent() const; - QDeclarativeDomComponent toComponent() const; - - int position() const; - int length() const; - - QUrl url() const; -private: - friend class QDeclarativeDomDocument; - friend class QDeclarativeDomComponent; - friend class QDeclarativeDomValue; - friend class QDeclarativeDomValueValueSource; - friend class QDeclarativeDomValueValueInterceptor; - QSharedDataPointer<QDeclarativeDomObjectPrivate> d; -}; - -class QDeclarativeDomValuePrivate; -class QDeclarativeDomBasicValuePrivate; -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValueLiteral -{ -public: - QDeclarativeDomValueLiteral(); - QDeclarativeDomValueLiteral(const QDeclarativeDomValueLiteral &); - ~QDeclarativeDomValueLiteral(); - QDeclarativeDomValueLiteral &operator=(const QDeclarativeDomValueLiteral &); - - QString literal() const; - -private: - friend class QDeclarativeDomValue; - QSharedDataPointer<QDeclarativeDomBasicValuePrivate> d; -}; - -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValueBinding -{ -public: - QDeclarativeDomValueBinding(); - QDeclarativeDomValueBinding(const QDeclarativeDomValueBinding &); - ~QDeclarativeDomValueBinding(); - QDeclarativeDomValueBinding &operator=(const QDeclarativeDomValueBinding &); - - QString binding() const; - -private: - friend class QDeclarativeDomValue; - QSharedDataPointer<QDeclarativeDomBasicValuePrivate> d; -}; - -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValueValueSource -{ -public: - QDeclarativeDomValueValueSource(); - QDeclarativeDomValueValueSource(const QDeclarativeDomValueValueSource &); - ~QDeclarativeDomValueValueSource(); - QDeclarativeDomValueValueSource &operator=(const QDeclarativeDomValueValueSource &); - - QDeclarativeDomObject object() const; - -private: - friend class QDeclarativeDomValue; - QSharedDataPointer<QDeclarativeDomBasicValuePrivate> d; -}; - -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValueValueInterceptor -{ -public: - QDeclarativeDomValueValueInterceptor(); - QDeclarativeDomValueValueInterceptor(const QDeclarativeDomValueValueInterceptor &); - ~QDeclarativeDomValueValueInterceptor(); - QDeclarativeDomValueValueInterceptor &operator=(const QDeclarativeDomValueValueInterceptor &); - - QDeclarativeDomObject object() const; - -private: - friend class QDeclarativeDomValue; - QSharedDataPointer<QDeclarativeDomBasicValuePrivate> d; -}; - - -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomComponent : public QDeclarativeDomObject -{ -public: - QDeclarativeDomComponent(); - QDeclarativeDomComponent(const QDeclarativeDomComponent &); - ~QDeclarativeDomComponent(); - QDeclarativeDomComponent &operator=(const QDeclarativeDomComponent &); - - QDeclarativeDomObject componentRoot() const; -}; - -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValue -{ -public: - enum Type { - Invalid, - Literal, - PropertyBinding, - ValueSource, - ValueInterceptor, - Object, - List - }; - - QDeclarativeDomValue(); - QDeclarativeDomValue(const QDeclarativeDomValue &); - ~QDeclarativeDomValue(); - QDeclarativeDomValue &operator=(const QDeclarativeDomValue &); - - Type type() const; - - bool isInvalid() const; - bool isLiteral() const; - bool isBinding() const; - bool isValueSource() const; - bool isValueInterceptor() const; - bool isObject() const; - bool isList() const; - - QDeclarativeDomValueLiteral toLiteral() const; - QDeclarativeDomValueBinding toBinding() const; - QDeclarativeDomValueValueSource toValueSource() const; - QDeclarativeDomValueValueInterceptor toValueInterceptor() const; - QDeclarativeDomObject toObject() const; - QDeclarativeDomList toList() const; - - int position() const; - int length() const; - -private: - friend class QDeclarativeDomProperty; - friend class QDeclarativeDomList; - QSharedDataPointer<QDeclarativeDomValuePrivate> d; -}; - -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomList -{ -public: - QDeclarativeDomList(); - QDeclarativeDomList(const QDeclarativeDomList &); - ~QDeclarativeDomList(); - QDeclarativeDomList &operator=(const QDeclarativeDomList &); - - QList<QDeclarativeDomValue> values() const; - - int position() const; - int length() const; - - QList<int> commaPositions() const; - -private: - friend class QDeclarativeDomValue; - QSharedDataPointer<QDeclarativeDomValuePrivate> d; -}; - -class QDeclarativeDomImportPrivate; -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomImport -{ -public: - enum Type { Library, File }; - - QDeclarativeDomImport(); - QDeclarativeDomImport(const QDeclarativeDomImport &); - ~QDeclarativeDomImport(); - QDeclarativeDomImport &operator=(const QDeclarativeDomImport &); - - Type type() const; - QString uri() const; - QString version() const; - QString qualifier() const; - -private: - friend class QDeclarativeDomDocument; - QSharedDataPointer<QDeclarativeDomImportPrivate> d; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QDECLARATIVEDOM_P_H diff --git a/src/declarative/qml/qdeclarativedom_p_p.h b/src/declarative/qml/qdeclarativedom_p_p.h deleted file mode 100644 index 7ce99ec74d..0000000000 --- a/src/declarative/qml/qdeclarativedom_p_p.h +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 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$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, 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. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QDECLARATIVEDOM_P_P_H -#define QDECLARATIVEDOM_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qdeclarativeparser_p.h" - -#include <QtCore/QtGlobal> - -QT_BEGIN_NAMESPACE - -class QDeclarativeDomDocumentPrivate : public QSharedData -{ -public: - QDeclarativeDomDocumentPrivate(); - QDeclarativeDomDocumentPrivate(const QDeclarativeDomDocumentPrivate &o) - : QSharedData(o) { qFatal("Not impl"); } - ~QDeclarativeDomDocumentPrivate(); - - QList<QDeclarativeError> errors; - QList<QDeclarativeDomImport> imports; - QDeclarativeParser::Object *root; - QList<int> automaticSemicolonOffsets; -}; - -class QDeclarativeDomObjectPrivate : public QSharedData -{ -public: - QDeclarativeDomObjectPrivate(); - QDeclarativeDomObjectPrivate(const QDeclarativeDomObjectPrivate &o) - : QSharedData(o) { qFatal("Not impl"); } - ~QDeclarativeDomObjectPrivate(); - - typedef QList<QPair<QDeclarativeParser::Property *, QByteArray> > Properties; - Properties properties() const; - Properties properties(QDeclarativeParser::Property *) const; - - QDeclarativeParser::Object *object; -}; - -class QDeclarativeDomPropertyPrivate : public QSharedData -{ -public: - QDeclarativeDomPropertyPrivate(); - QDeclarativeDomPropertyPrivate(const QDeclarativeDomPropertyPrivate &o) - : QSharedData(o) { qFatal("Not impl"); } - ~QDeclarativeDomPropertyPrivate(); - - QByteArray propertyName; - QDeclarativeParser::Property *property; -}; - -class QDeclarativeDomDynamicPropertyPrivate : public QSharedData -{ -public: - QDeclarativeDomDynamicPropertyPrivate(); - QDeclarativeDomDynamicPropertyPrivate(const QDeclarativeDomDynamicPropertyPrivate &o) - : QSharedData(o) { qFatal("Not impl"); } - ~QDeclarativeDomDynamicPropertyPrivate(); - - bool valid; - QDeclarativeParser::Object::DynamicProperty property; -}; - -class QDeclarativeDomValuePrivate : public QSharedData -{ -public: - QDeclarativeDomValuePrivate(); - QDeclarativeDomValuePrivate(const QDeclarativeDomValuePrivate &o) - : QSharedData(o) { qFatal("Not impl"); } - ~QDeclarativeDomValuePrivate(); - - QDeclarativeParser::Property *property; - QDeclarativeParser::Value *value; -}; - -class QDeclarativeDomBasicValuePrivate : public QSharedData -{ -public: - QDeclarativeDomBasicValuePrivate(); - QDeclarativeDomBasicValuePrivate(const QDeclarativeDomBasicValuePrivate &o) - : QSharedData(o) { qFatal("Not impl"); } - ~QDeclarativeDomBasicValuePrivate(); - - QDeclarativeParser::Value *value; -}; - -class QDeclarativeDomImportPrivate : public QSharedData -{ -public: - QDeclarativeDomImportPrivate(); - QDeclarativeDomImportPrivate(const QDeclarativeDomImportPrivate &o) - : QSharedData(o) { qFatal("Not impl"); } - ~QDeclarativeDomImportPrivate(); - - enum Type { Library, File }; - - Type type; - QString uri; - QString version; - QString qualifier; -}; - -QT_END_NAMESPACE - -#endif // QDECLARATIVEDOM_P_P_H - diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index 9fde18c81c..b30b4cfb55 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -55,6 +55,7 @@ #include "private/qdeclarativestringconverters_p.h" #include "private/qdeclarativexmlhttprequest_p.h" #include "private/qdeclarativesqldatabase_p.h" +#include "private/qdeclarativescarceresourcescriptclass_p.h" #include "private/qdeclarativetypenamescriptclass_p.h" #include "private/qdeclarativelistscriptclass_p.h" #include "qdeclarativescriptstring.h" @@ -103,6 +104,8 @@ #include <private/qdeclarativeitemsmodule_p.h> #include <private/qdeclarativeutilmodule_p.h> +#include <private/qsgitemsmodule_p.h> +#include <qsgtexture.h> #ifdef Q_OS_WIN // for %APPDATA% #include <qt_windows.h> @@ -349,13 +352,15 @@ QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e) objectClass(0), valueTypeClass(0), globalClass(0), cleanup(0), erroredBindings(0), inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttached(0), inBeginCreate(false), networkAccessManager(0), networkAccessManagerFactory(0), - typeLoader(e), importDatabase(e), uniqueId(1) + scarceResources(0), scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), + sgContext(0) { if (!qt_QmlQtModule_registered) { qt_QmlQtModule_registered = true; QDeclarativeItemModule::defineModule(); QDeclarativeUtilModule::defineModule(); QDeclarativeEnginePrivate::defineModule(); + QSGItemsModule::defineModule(); QDeclarativeValueTypeFactory::registerValueTypes(); } globalClass = new QDeclarativeGlobalScriptClass(&scriptEngine); @@ -499,6 +504,8 @@ QDeclarativeEnginePrivate::~QDeclarativeEnginePrivate() contextClass = 0; delete objectClass; objectClass = 0; + delete scarceResourceClass; + scarceResourceClass = 0; delete valueTypeClass; valueTypeClass = 0; delete typeNameClass; @@ -514,7 +521,10 @@ QDeclarativeEnginePrivate::~QDeclarativeEnginePrivate() (*iter)->release(); for(QHash<QPair<QDeclarativeType *, int>, QDeclarativePropertyCache *>::Iterator iter = typePropertyCache.begin(); iter != typePropertyCache.end(); ++iter) (*iter)->release(); - + for(QHash<QDeclarativeMetaType::ModuleApi, QDeclarativeMetaType::ModuleApiInstance *>::Iterator iter = moduleApiInstances.begin(); iter != moduleApiInstances.end(); ++iter) { + delete (*iter)->qobjectApi; + delete *iter; + } } void QDeclarativeEnginePrivate::clear(SimpleList<QDeclarativeAbstractBinding> &bvs) @@ -571,6 +581,7 @@ void QDeclarativeEnginePrivate::init() contextClass = new QDeclarativeContextScriptClass(q); objectClass = new QDeclarativeObjectScriptClass(q); + scarceResourceClass = new QDeclarativeScarceResourceScriptClass(q); valueTypeClass = new QDeclarativeValueTypeScriptClass(q); typeNameClass = new QDeclarativeTypeNameScriptClass(q); listClass = new QDeclarativeListScriptClass(q); @@ -647,6 +658,22 @@ QDeclarativeEngine::~QDeclarativeEngine() Q_D(QDeclarativeEngine); if (d->isDebugging) QDeclarativeEngineDebugServer::instance()->remEngine(this); + + // if we are the parent of any of the qobject module api instances, + // we need to remove them from our internal list, in order to prevent + // a segfault in engine private dtor. + QList<QDeclarativeMetaType::ModuleApi> keys = d->moduleApiInstances.keys(); + QObject *currQObjectApi = 0; + QDeclarativeMetaType::ModuleApiInstance *currInstance = 0; + foreach (const QDeclarativeMetaType::ModuleApi &key, keys) { + currInstance = d->moduleApiInstances.value(key); + currQObjectApi = currInstance->qobjectApi; + if (this->children().contains(currQObjectApi)) { + delete currQObjectApi; + delete currInstance; + d->moduleApiInstances.remove(key); + } + } } /*! \fn void QDeclarativeEngine::quit() @@ -813,6 +840,18 @@ QDeclarativeImageProvider::ImageType QDeclarativeEnginePrivate::getImageProvider return static_cast<QDeclarativeImageProvider::ImageType>(-1); } +QSGTexture *QDeclarativeEnginePrivate::getTextureFromProvider(const QUrl &url, QSize *size, const QSize& req_size) +{ + QMutexLocker locker(&mutex); + QSharedPointer<QDeclarativeImageProvider> provider = imageProviders.value(url.host()); + locker.unlock(); + if (provider) { + QString imageId = url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); + return provider->requestTexture(imageId, size, req_size); + } + return 0; +} + QImage QDeclarativeEnginePrivate::getImageFromProvider(const QUrl &url, QSize *size, const QSize& req_size) { QMutexLocker locker(&mutex); @@ -2056,7 +2095,9 @@ QScriptValue QDeclarativeEnginePrivate::tint(QScriptContext *ctxt, QScriptEngine QScriptValue QDeclarativeEnginePrivate::scriptValueFromVariant(const QVariant &val) { - if (val.userType() == qMetaTypeId<QDeclarativeListReference>()) { + if (variantIsScarceResource(val)) { + return scarceResourceClass->newScarceResource(val); + } else if (val.userType() == qMetaTypeId<QDeclarativeListReference>()) { QDeclarativeListReferencePrivate *p = QDeclarativeListReferencePrivate::get((QDeclarativeListReference*)val.constData()); if (p->object) { @@ -2085,11 +2126,69 @@ QScriptValue QDeclarativeEnginePrivate::scriptValueFromVariant(const QVariant &v } } +/* + If the variant is a scarce resource (consumes a large amount of memory, or + only a limited number of them can be held in memory at any given time without + exhausting supply for future use) we need to release the scarce resource + after evaluation of the javascript binding is complete. + */ +bool QDeclarativeEnginePrivate::variantIsScarceResource(const QVariant& val) +{ + if (val.type() == QVariant::Pixmap) { + return true; + } else if (val.type() == QVariant::Image) { + return true; + } + + return false; +} + +/* + This function should be called prior to evaluation of any js expression, + so that scarce resources are not freed prematurely (eg, if there is a + nested javascript expression). + */ +void QDeclarativeEnginePrivate::referenceScarceResources() +{ + scarceResourcesRefCount += 1; +} + +/* + This function should be called after evaluation of the js expression is + complete, and so the scarce resources may be freed safely. + */ +void QDeclarativeEnginePrivate::dereferenceScarceResources() +{ + Q_ASSERT(scarceResourcesRefCount > 0); + scarceResourcesRefCount -= 1; + + // if the refcount is zero, then evaluation of the "top level" + // expression must have completed. We can safely release the + // scarce resources. + if (scarceResourcesRefCount == 0) { + // iterate through the list and release them all. + // note that the actual SRD is owned by the JS engine, + // so we cannot delete the SRD; but we can free the + // memory used by the variant in the SRD. + ScarceResourceData *srd = 0; + while (scarceResources) { + srd = scarceResources; // srd points to the "old" (current) head of the list + scarceResources = srd->next; // srd->next is the "new" head of the list + if (srd->next) srd->next->prev = &scarceResources; // newHead->prev = listptr. + srd->next = 0; + srd->prev = 0; + srd->releaseResource(); // release the old head node. + } + } +} + QVariant QDeclarativeEnginePrivate::scriptValueToVariant(const QScriptValue &val, int hint) { QScriptDeclarativeClass *dc = QScriptDeclarativeClass::scriptClass(val); if (dc == objectClass) return QVariant::fromValue(objectClass->toQObject(val)); + else if (dc == scarceResourceClass) + return scarceResourceClass->toVariant(val); else if (dc == valueTypeClass) return valueTypeClass->toVariant(val); else if (dc == contextClass) @@ -2218,6 +2317,20 @@ void QDeclarativeEngine::setPluginPathList(const QStringList &paths) Imports the plugin named \a filePath with the \a uri provided. Returns true if the plugin was successfully imported; otherwise returns false. + On failure and if non-null, the \a errors list will have any errors which occurred prepended to it. + + The plugin has to be a Qt plugin which implements the QDeclarativeExtensionPlugin interface. +*/ +bool QDeclarativeEngine::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors) +{ + Q_D(QDeclarativeEngine); + return d->importDatabase.importPlugin(filePath, uri, errors); +} + +/*! + Imports the plugin named \a filePath with the \a uri provided. + Returns true if the plugin was successfully imported; otherwise returns false. + On failure and if non-null, *\a errorString will be set to a message describing the failure. The plugin has to be a Qt plugin which implements the QDeclarativeExtensionPlugin interface. @@ -2225,7 +2338,18 @@ void QDeclarativeEngine::setPluginPathList(const QStringList &paths) bool QDeclarativeEngine::importPlugin(const QString &filePath, const QString &uri, QString *errorString) { Q_D(QDeclarativeEngine); - return d->importDatabase.importPlugin(filePath, uri, errorString); + QList<QDeclarativeError> errors; + bool retn = d->importDatabase.importPlugin(filePath, uri, &errors); + if (!errors.isEmpty()) { + QString builtError; + for (int i = 0; i < errors.size(); ++i) { + builtError = QString(QLatin1String("%1\n %2")) + .arg(builtError) + .arg(errors.at(i).toString()); + } + *errorString = builtError; + } + return retn; } /*! diff --git a/src/declarative/qml/qdeclarativeengine.h b/src/declarative/qml/qdeclarativeengine.h index 631fc5ec57..8db3db8096 100644 --- a/src/declarative/qml/qdeclarativeengine.h +++ b/src/declarative/qml/qdeclarativeengine.h @@ -86,7 +86,8 @@ public: void setPluginPathList(const QStringList &paths); void addPluginPath(const QString& dir); - bool importPlugin(const QString &filePath, const QString &uri, QString *errorString); + bool importPlugin(const QString &filePath, const QString &uri, QString *errorString); // XXX: Qt 5: Remove this function + bool importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors); void setNetworkAccessManagerFactory(QDeclarativeNetworkAccessManagerFactory *); QDeclarativeNetworkAccessManagerFactory *networkAccessManagerFactory() const; diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h index 88b4e800f8..fadedf41dd 100644 --- a/src/declarative/qml/qdeclarativeengine_p.h +++ b/src/declarative/qml/qdeclarativeengine_p.h @@ -67,6 +67,7 @@ #include "private/qdeclarativeproperty_p.h" #include "private/qdeclarativepropertycache_p.h" #include "private/qdeclarativeobjectscriptclass_p.h" +#include "private/qdeclarativescarceresourcescriptclass_p.h" #include "private/qdeclarativecontextscriptclass_p.h" #include "private/qdeclarativevaluetypescriptclass_p.h" #include "private/qdeclarativemetatype_p.h" @@ -93,6 +94,8 @@ class QDeclarativeExpression; class QDeclarativeContextScriptClass; class QDeclarativeImportDatabase; class QDeclarativeObjectScriptClass; +class QDeclarativeScarceResourceScriptClass; +class ScarceResourceData; class QDeclarativeTypeNameScriptClass; class QDeclarativeValueTypeScriptClass; class QScriptEngineDebugger; @@ -110,6 +113,8 @@ class QDeclarativeDelayedError; class QDeclarativeWorkerScriptEngine; class QDeclarativeGlobalScriptClass; class QDir; +class QSGTexture; +class QSGContext; class QDeclarativeScriptEngine : public QScriptEngine { @@ -169,6 +174,7 @@ public: QDeclarativeContextData *sharedContext; QObject *sharedScope; QDeclarativeObjectScriptClass *objectClass; + QDeclarativeScarceResourceScriptClass *scarceResourceClass; QDeclarativeValueTypeScriptClass *valueTypeClass; QDeclarativeTypeNameScriptClass *typeNameClass; QDeclarativeListScriptClass *listClass; @@ -233,9 +239,22 @@ public: QHash<QString,QSharedPointer<QDeclarativeImageProvider> > imageProviders; QDeclarativeImageProvider::ImageType getImageProviderType(const QUrl &url); + QSGTexture *getTextureFromProvider(const QUrl &url, QSize *size, const QSize& req_size); QImage getImageFromProvider(const QUrl &url, QSize *size, const QSize& req_size); QPixmap getPixmapFromProvider(const QUrl &url, QSize *size, const QSize& req_size); + /* + A scarce resource (like a large pixmap or texture) will be cached in a + JavaScript wrapper object when accessed in a binding or other js expression. + We need some way to automatically release that scarce resource prior to normal + garbage collection (unless the user explicitly preserves the resource). + */ + ScarceResourceData* scarceResources; + int scarceResourcesRefCount; + static bool variantIsScarceResource(const QVariant& val); + void referenceScarceResources(); + void dereferenceScarceResources(); + mutable QMutex mutex; QDeclarativeTypeLoader typeLoader; @@ -250,6 +269,8 @@ public: QDeclarativeValueTypeFactory valueTypes; + QHash<QDeclarativeMetaType::ModuleApi, QDeclarativeMetaType::ModuleApiInstance *> moduleApiInstances; + QHash<const QMetaObject *, QDeclarativePropertyCache *> propertyCache; QHash<QPair<QDeclarativeType *, int>, QDeclarativePropertyCache *> typePropertyCache; inline QDeclarativePropertyCache *cache(QObject *obj); @@ -270,8 +291,6 @@ public: QHash<int, int> m_qmlLists; QHash<int, QDeclarativeCompiledData *> m_compositeTypes; - QHash<QString, QScriptValue> m_sharedScriptImports; - QScriptValue scriptValueFromVariant(const QVariant &); QVariant scriptValueToVariant(const QScriptValue &, int hint = QVariant::Invalid); @@ -327,6 +346,8 @@ public: static void defineModule(); static bool qml_debugging_enabled; + + QSGContext *sgContext; }; /*! diff --git a/src/declarative/qml/qdeclarativeexpression.cpp b/src/declarative/qml/qdeclarativeexpression.cpp index 7a85adaa6e..afd1be1025 100644 --- a/src/declarative/qml/qdeclarativeexpression.cpp +++ b/src/declarative/qml/qdeclarativeexpression.cpp @@ -630,7 +630,6 @@ QScriptValue QDeclarativeExpressionPrivate::scriptValue(QObject *secondaryScope, if (!expressionFunctionValid) { QDeclarativeEngine *engine = context()->engine; QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); @@ -663,8 +662,10 @@ QVariant QDeclarativeExpressionPrivate::value(QObject *secondaryScope, bool *isU } QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(q->engine()); - - return ep->scriptValueToVariant(scriptValue(secondaryScope, isUndefined), qMetaTypeId<QList<QObject*> >()); + ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. + QVariant retn(ep->scriptValueToVariant(scriptValue(secondaryScope, isUndefined), qMetaTypeId<QList<QObject*> >())); + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. + return retn; } /*! diff --git a/src/declarative/qml/qdeclarativeimageprovider.cpp b/src/declarative/qml/qdeclarativeimageprovider.cpp index f111c201fa..c9b399cc32 100644 --- a/src/declarative/qml/qdeclarativeimageprovider.cpp +++ b/src/declarative/qml/qdeclarativeimageprovider.cpp @@ -159,6 +159,8 @@ public: requestImage() method will be called for all image requests. \value Pixmap The Image Provider provides QPixmap images. The requestPixmap() method will be called for all image requests. + \value Texture The Image Provider provides QSGTextureProvider based images. + The requestTexture() method will be called for all image requests. \omitvalue */ /*! @@ -243,5 +245,36 @@ QPixmap QDeclarativeImageProvider::requestPixmap(const QString &id, QSize *size, return QPixmap(); } + +/*! + Implement this method to return the texture with \a id. The default + implementation returns 0. + + The \a id is the requested image source, with the "image:" scheme and + provider identifier removed. For example, if the image \l{Image::}{source} + was "image://myprovider/icons/home", the given \a id would be "icons/home". + + The \a requestedSize corresponds to the \l {Image::sourceSize} requested by + an Image element. If \a requestedSize is a valid size, the image + returned should be of that size. + + In all cases, \a size must be set to the original size of the image. This + is used to set the \l {Item::}{width} and \l {Item::}{height} of the + relevant \l Image if these values have not been set explicitly. + + \note this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ + +QSGTexture *QDeclarativeImageProvider::requestTexture(const QString &id, QSize *size, const QSize &requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(size); + Q_UNUSED(requestedSize); + if (d->type == Texture) + qWarning("ImageProvider supports Texture type but has not implemented requestTexture()"); + return 0; +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeimageprovider.h b/src/declarative/qml/qdeclarativeimageprovider.h index 7f13fda85b..e5e80f227c 100644 --- a/src/declarative/qml/qdeclarativeimageprovider.h +++ b/src/declarative/qml/qdeclarativeimageprovider.h @@ -52,13 +52,15 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QDeclarativeImageProviderPrivate; +class QSGTexture; class Q_DECLARATIVE_EXPORT QDeclarativeImageProvider { public: enum ImageType { Image, - Pixmap + Pixmap, + Texture }; QDeclarativeImageProvider(ImageType type); @@ -68,6 +70,7 @@ public: virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize); virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize); + virtual QSGTexture *requestTexture(const QString &id, QSize *size, const QSize &requestedSize); private: QDeclarativeImageProviderPrivate *d; diff --git a/src/declarative/qml/qdeclarativeimport.cpp b/src/declarative/qml/qdeclarativeimport.cpp index e8d593fd1a..c5abe2c75d 100644 --- a/src/declarative/qml/qdeclarativeimport.cpp +++ b/src/declarative/qml/qdeclarativeimport.cpp @@ -83,7 +83,7 @@ public: QDeclarativeType** type_return, QUrl* url_return, QUrl *base = 0, bool *typeRecursionDetected = 0); bool find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, - QUrl* url_return, QUrl *base = 0, QString *errorString = 0); + QUrl* url_return, QUrl *base = 0, QList<QDeclarativeError> *errors = 0); }; class QDeclarativeImportsPrivate { @@ -93,15 +93,15 @@ public: bool importExtension(const QString &absoluteFilePath, const QString &uri, QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components, - QString *errorString); + QList<QDeclarativeError> *errors); QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database); bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork, const QString& uri_arg, const QString& prefix, int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, - QDeclarativeImportDatabase *database, QString *errorString); + QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors); bool find(const QByteArray& type, int *vmajor, int *vminor, - QDeclarativeType** type_return, QUrl* url_return, QString *errorString); + QDeclarativeType** type_return, QUrl* url_return, QList<QDeclarativeError> *errors); QDeclarativeImportedNamespace *findNamespace(const QString& type); @@ -163,7 +163,7 @@ QUrl QDeclarativeImports::baseUrl() const static QDeclarativeTypeNameCache * cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeImportedNamespace &set, - QDeclarativeTypeNameCache *cache) + QDeclarativeTypeNameCache *cache, bool importWasQualified) { if (!cache) cache = new QDeclarativeTypeNameCache(engine); @@ -171,10 +171,27 @@ cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeImportedNamespac QList<QDeclarativeType *> types = QDeclarativeMetaType::qmlTypes(); for (int ii = 0; ii < set.uris.count(); ++ii) { - QByteArray base = set.uris.at(ii).toUtf8() + '/'; + QByteArray uri = set.uris.at(ii).toUtf8(); int major = set.majversions.at(ii); int minor = set.minversions.at(ii); + if (importWasQualified) { + QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(uri, major, minor); + if (moduleApi.script || moduleApi.qobject) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeMetaType::ModuleApiInstance *a = ep->moduleApiInstances.value(moduleApi); + if (!a) { + a = new QDeclarativeMetaType::ModuleApiInstance; + a->scriptCallback = moduleApi.script; + a->qobjectCallback = moduleApi.qobject; + ep->moduleApiInstances.insert(moduleApi, a); + } + cache->setModuleApi(a); + } + } + + QByteArray base = uri + '/'; + foreach (QDeclarativeType *type, types) { if (type->qmlTypeName().startsWith(base) && type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) && @@ -200,15 +217,15 @@ void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDecla QDeclarativeTypeNameCache::Data *d = cache->data(iter.key()); if (d) { if (!d->typeNamespace) - cacheForNamespace(engine, *(*iter), d->typeNamespace); + cacheForNamespace(engine, *(*iter), d->typeNamespace, true); } else { - QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0); + QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0, true); cache->add(iter.key(), nc); nc->release(); } } - cacheForNamespace(engine, set, cache); + cacheForNamespace(engine, set, cache, false); } /*! @@ -227,7 +244,7 @@ void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDecla */ bool QDeclarativeImports::resolveType(const QByteArray& type, QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin, - QDeclarativeImportedNamespace** ns_return, QString *errorString) const + QDeclarativeImportedNamespace** ns_return, QList<QDeclarativeError> *errors) const { QDeclarativeImportedNamespace* ns = d->findNamespace(QString::fromUtf8(type)); if (ns) { @@ -236,7 +253,7 @@ bool QDeclarativeImports::resolveType(const QByteArray& type, return true; } if (type_return || url_return) { - if (d->find(type,vmaj,vmin,type_return,url_return, errorString)) { + if (d->find(type,vmaj,vmin,type_return,url_return, errors)) { if (qmlImportTrace()) { if (type_return && *type_return && url_return && !url_return->isEmpty()) qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " @@ -351,13 +368,16 @@ QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate() bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri, QDeclarativeImportDatabase *database, - QDeclarativeDirComponents* components, QString *errorString) + QDeclarativeDirComponents* components, QList<QDeclarativeError> *errors) { QFile file(absoluteFilePath); QString filecontent; if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) { - if (errorString) - *errorString = QDeclarativeImportDatabase::tr("cannot load module \"%1\": File name case mismatch for \"%2\"").arg(uri).arg(absoluteFilePath); + if (errors) { + QDeclarativeError error; + error.setDescription(QDeclarativeImportDatabase::tr("cannot load module \"%1\": File name case mismatch for \"%2\"").arg(uri).arg(absoluteFilePath)); + errors->prepend(error); + } return false; } else if (file.open(QFile::ReadOnly)) { filecontent = QString::fromUtf8(file.readAll()); @@ -365,15 +385,29 @@ bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath qDebug().nospace() << "QDeclarativeImports(" << qPrintable(base.toString()) << "::importExtension: " << "loaded " << absoluteFilePath; } else { - if (errorString) - *errorString = QDeclarativeImportDatabase::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath); + if (errors) { + QDeclarativeError error; + error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath)); + errors->prepend(error); + } return false; } QDir dir = QFileInfo(file).dir(); + QUrl url = QUrl::fromLocalFile(absoluteFilePath); QDeclarativeDirParser qmldirParser; qmldirParser.setSource(filecontent); - qmldirParser.parse(); + qmldirParser.setUrl(url); + + // propagate any errors reported by the parser back up to the typeloader. + if (qmldirParser.parse()) { + if (errors) { + for (int i = 0; i < qmldirParser.errors().size(); ++i) { + errors->prepend(qmldirParser.errors().at(i)); + } + } + return false; + } if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) { qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath); @@ -390,14 +424,26 @@ bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath } #endif if (!resolvedFilePath.isEmpty()) { - if (!database->importPlugin(resolvedFilePath, uri, errorString)) { - if (errorString) - *errorString = QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(*errorString); + if (!database->importPlugin(resolvedFilePath, uri, errors)) { + if (errors) { + // XXX TODO: should we leave the import plugin error alone? + // Here, we pop it off the top and coalesce it into this error's message. + // The reason is that the lower level may add url and line/column numbering information. + QDeclarativeError poppedError = errors->takeFirst(); + QDeclarativeError error; + error.setDescription(QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description())); + error.setUrl(url); + errors->prepend(error); + } return false; } } else { - if (errorString) - *errorString = QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name); + if (errors) { + QDeclarativeError error; + error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name)); + error.setUrl(url); + errors->prepend(error); + } return false; } } @@ -443,7 +489,7 @@ QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclara bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork, const QString& uri_arg, const QString& prefix, int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, - QDeclarativeImportDatabase *database, QString *errorString) + QDeclarativeImportDatabase *database, QList<QDeclarativeError> *errors) { QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork; QString uri = uri_arg; @@ -477,7 +523,7 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp url = QUrl::fromLocalFile(fi.absolutePath()).toString(); uri = resolvedUri(dir, database); - if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors)) return false; break; } @@ -496,7 +542,7 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp url = QUrl::fromLocalFile(fi.absolutePath()).toString(); uri = resolvedUri(dir, database); - if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors)) return false; break; } @@ -516,7 +562,7 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp url = QUrl::fromLocalFile(fi.absolutePath()).toString(); uri = resolvedUri(dir, database); - if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errors)) return false; break; } @@ -527,12 +573,14 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp versionFound = true; if (!versionFound && qmldircomponents.isEmpty()) { - if (errorString) { + if (errors) { bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1); + QDeclarativeError error; // we don't set the url or line or column as these will be set by the loader. if (anyversion) - *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); + error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin)); else - *errorString = QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg); + error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg)); + errors->prepend(error); } return false; } @@ -545,15 +593,19 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))); QFileInfo dirinfo(dir); if (dir.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) { - if (errorString) - *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg); + if (errors) { + QDeclarativeError error; // we don't set the line or column as these will be set by the loader. + error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg)); + error.setUrl(importUrl); + errors->prepend(error); + } return false; // local import dirs must exist } uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database); if (uri.endsWith(QLatin1Char('/'))) uri.chop(1); if (QFile::exists(localFileOrQrc)) { - if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errorString)) + if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errors)) return false; } } else { @@ -562,11 +614,14 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))); QFileInfo dirinfo(localFileOrQrc); if (localFileOrQrc.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) { - if (errorString) { + if (errors) { + QDeclarativeError error; // we don't set the line or column as these will be set by the loader. if (localFileOrQrc.isEmpty()) - *errorString = QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri); + error.setDescription(QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri)); else - *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri); + error.setDescription(QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri)); + error.setUrl(importUrl); + errors->prepend(error); } return false; } @@ -598,7 +653,11 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp if (lowest_maj > vmaj || (lowest_maj == vmaj && lowest_min > vmin) || highest_maj < vmaj || (highest_maj == vmaj && highest_min < vmin)) { - *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); + if (errors) { + QDeclarativeError error; // we don't set the url or line or column information, as these will be set by the loader. + error.setDescription(QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin)); + errors->prepend(error); + } return false; } } @@ -613,7 +672,7 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp } bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, - QUrl* url_return, QString *errorString) + QUrl* url_return, QList<QDeclarativeError> *errors) { QDeclarativeImportedNamespace *s = 0; int slash = type.indexOf('/'); @@ -621,14 +680,20 @@ bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int * QString namespaceName = QString::fromUtf8(type.left(slash)); s = set.value(namespaceName); if (!s) { - if (errorString) - *errorString = QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName); + if (errors) { + QDeclarativeError error; + error.setDescription(QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName)); + errors->prepend(error); + } return false; } int nslash = type.indexOf('/',slash+1); if (nslash > 0) { - if (errorString) - *errorString = QDeclarativeImportDatabase::tr("- nested namespaces not allowed"); + if (errors) { + QDeclarativeError error; + error.setDescription(QDeclarativeImportDatabase::tr("- nested namespaces not allowed")); + errors->prepend(error); + } return false; } } else { @@ -636,7 +701,7 @@ bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int * } QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower) if (s) { - if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errorString)) + if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors)) return true; if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) { // qualified, and only 1 url @@ -654,7 +719,7 @@ QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const Q } bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, - QUrl* url_return, QUrl *base, QString *errorString) + QUrl* url_return, QUrl *base, QList<QDeclarativeError> *errors) { bool typeRecursionDetected = false; for (int i=0; i<urls.count(); ++i) { @@ -663,7 +728,7 @@ bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, in // check for type clashes for (int j = i+1; j<urls.count(); ++j) { if (find_helper(j, type, vmajor, vminor, 0, 0, base)) { - if (errorString) { + if (errors) { QString u1 = urls.at(i); QString u2 = urls.at(j); if (base) { @@ -683,16 +748,16 @@ bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, in } } - if (u1 != u2) - *errorString - = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2") - .arg(u1).arg(u2); - else - *errorString - = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5") - .arg(u1) - .arg(majversions.at(i)).arg(minversions.at(i)) - .arg(majversions.at(j)).arg(minversions.at(j)); + QDeclarativeError error; + if (u1 != u2) { + error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2)); + } else { + error.setDescription(QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5") + .arg(u1) + .arg(majversions.at(i)).arg(minversions.at(i)) + .arg(majversions.at(j)).arg(minversions.at(j))); + } + errors->prepend(error); } return false; } @@ -701,11 +766,13 @@ bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, in return true; } } - if (errorString) { + if (errors) { + QDeclarativeError error; if (typeRecursionDetected) - *errorString = QDeclarativeImportDatabase::tr("is instantiated recursively"); + error.setDescription(QDeclarativeImportDatabase::tr("is instantiated recursively")); else - *errorString = QDeclarativeImportDatabase::tr("is not a type"); + error.setDescription(QDeclarativeImportDatabase::tr("is not a type")); + errors->prepend(error); } return false; } @@ -790,7 +857,7 @@ bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb, const QString& uri, const QString& prefix, int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, const QDeclarativeDirComponents &qmldircomponentsnetwork, - QString *errorString) + QList<QDeclarativeError> *errors) { if (qmlImportTrace()) qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: " @@ -798,7 +865,7 @@ bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb, << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File") << " as " << prefix; - return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errorString); + return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors); } /*! @@ -1013,7 +1080,7 @@ void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths) /*! \internal */ -bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QString *errorString) +bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors) { if (qmlImportTrace()) qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath; @@ -1033,15 +1100,21 @@ bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QSt if (!engineInitialized || !typesRegistered) { if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) { - if (errorString) - *errorString = tr("File name case mismatch for \"%2\"").arg(absoluteFilePath); + if (errors) { + QDeclarativeError error; + error.setDescription(tr("File name case mismatch for \"%2\"").arg(absoluteFilePath)); + errors->prepend(error); + } return false; } QPluginLoader loader(absoluteFilePath); if (!loader.load()) { - if (errorString) - *errorString = loader.errorString(); + if (errors) { + QDeclarativeError error; + error.setDescription(loader.errorString()); + errors->prepend(error); + } return false; } @@ -1063,8 +1136,11 @@ bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QSt iface->initializeEngine(engine, moduleId); } } else { - if (errorString) - *errorString = loader.errorString(); + if (errors) { + QDeclarativeError error; + error.setDescription(loader.errorString()); + errors->prepend(error); + } return false; } } diff --git a/src/declarative/qml/qdeclarativeimport_p.h b/src/declarative/qml/qdeclarativeimport_p.h index 1c910fd5bf..9d140bf106 100644 --- a/src/declarative/qml/qdeclarativeimport_p.h +++ b/src/declarative/qml/qdeclarativeimport_p.h @@ -84,7 +84,7 @@ public: QDeclarativeType** type_return, QUrl* url_return, int *version_major, int *version_minor, QDeclarativeImportedNamespace** ns_return, - QString *errorString = 0) const; + QList<QDeclarativeError> *errors = 0) const; bool resolveType(QDeclarativeImportedNamespace*, const QByteArray& type, QDeclarativeType** type_return, QUrl* url_return, @@ -94,7 +94,7 @@ public: const QString& uri, const QString& prefix, int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, const QDeclarativeDirComponents &qmldircomponentsnetwork, - QString *errorString); + QList<QDeclarativeError> *errors); void populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *) const; @@ -110,7 +110,7 @@ public: QDeclarativeImportDatabase(QDeclarativeEngine *); ~QDeclarativeImportDatabase(); - bool importPlugin(const QString &filePath, const QString &uri, QString *errorString); + bool importPlugin(const QString &filePath, const QString &uri, QList<QDeclarativeError> *errors); QStringList importPathList() const; void setImportPathList(const QStringList &paths); diff --git a/src/declarative/qml/qdeclarativeinfo.cpp b/src/declarative/qml/qdeclarativeinfo.cpp index 7c8f73bc61..8449578c65 100644 --- a/src/declarative/qml/qdeclarativeinfo.cpp +++ b/src/declarative/qml/qdeclarativeinfo.cpp @@ -129,6 +129,18 @@ QDeclarativeInfo::~QDeclarativeInfo() int marker = typeName.indexOf(QLatin1String("_QMLTYPE_")); if (marker != -1) typeName = typeName.left(marker); + + marker = typeName.indexOf(QLatin1String("_QML_")); + if (marker != -1) { + typeName = typeName.left(marker) + "*"; + type = QDeclarativeMetaType::qmlType(QMetaType::type(typeName.toLatin1())); + if (type) { + typeName = QLatin1String(type->qmlTypeName()); + int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + if (lastSlash != -1) + typeName = typeName.mid(lastSlash+1); + } + } } d->buffer.prepend(QLatin1String("QML ") + typeName + QLatin1String(": ")); diff --git a/src/declarative/qml/qdeclarativeinstruction.cpp b/src/declarative/qml/qdeclarativeinstruction.cpp index 0c99cefb04..556b7bc343 100644 --- a/src/declarative/qml/qdeclarativeinstruction.cpp +++ b/src/declarative/qml/qdeclarativeinstruction.cpp @@ -96,6 +96,9 @@ void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx) case QDeclarativeInstruction::StoreString: qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_STRING\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); break; + case QDeclarativeInstruction::StoreByteArray: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_BYTEARRAY" << instr->storeByteArray.propertyIndex << "\t" << instr->storeByteArray.value << "\t\t" << datas.at(instr->storeByteArray.value); + break; case QDeclarativeInstruction::StoreUrl: qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_URL\t\t" << instr->storeUrl.propertyIndex << "\t" << instr->storeUrl.value << "\t\t" << urls.at(instr->storeUrl.value); break; diff --git a/src/declarative/qml/qdeclarativeinstruction_p.h b/src/declarative/qml/qdeclarativeinstruction_p.h index 20be889252..a5521b6425 100644 --- a/src/declarative/qml/qdeclarativeinstruction_p.h +++ b/src/declarative/qml/qdeclarativeinstruction_p.h @@ -88,6 +88,7 @@ public: // StoreInteger - Store a int or uint in a core property // StoreBool - Store a bool in a core property // StoreString - Store a QString in a core property + // StoreByteArray - Store a QByteArray in a core property // StoreUrl - Store a QUrl in a core property // StoreColor - Store a QColor in a core property // StoreDate - Store a QDate in a core property @@ -101,6 +102,7 @@ public: StoreInteger, /* storeInteger */ StoreBool, /* storeBool */ StoreString, /* storeString */ + StoreByteArray, /* storeByteArray */ StoreUrl, /* storeUrl */ StoreColor, /* storeColor */ StoreDate, /* storeDate */ @@ -245,6 +247,10 @@ public: int propertyIndex; int value; }; + struct StoreByteArrayInstruction { + int propertyIndex; + int value; + }; struct StoreScriptStringInstruction { int propertyIndex; int value; @@ -332,6 +338,7 @@ public: StoreIntegerInstruction storeInteger; StoreBoolInstruction storeBool; StoreStringInstruction storeString; + StoreByteArrayInstruction storeByteArray; StoreScriptStringInstruction storeScriptString; StoreScriptInstruction storeScript; StoreUrlInstruction storeUrl; diff --git a/src/declarative/qml/qdeclarativemetatype.cpp b/src/declarative/qml/qdeclarativemetatype.cpp index bf1f699c72..ede02e9f30 100644 --- a/src/declarative/qml/qdeclarativemetatype.cpp +++ b/src/declarative/qml/qdeclarativemetatype.cpp @@ -88,6 +88,7 @@ QT_BEGIN_NAMESPACE struct QDeclarativeMetaTypeData { + QDeclarativeMetaTypeData(); ~QDeclarativeMetaTypeData(); QList<QDeclarativeType *> types; typedef QHash<int, QDeclarativeType *> Ids; @@ -98,6 +99,14 @@ struct QDeclarativeMetaTypeData MetaObjects metaObjectToType; typedef QHash<int, QDeclarativeMetaType::StringConverter> StringConverters; StringConverters stringConverters; + struct ModuleApiList { + ModuleApiList() : sorted(true) {} + QList<QDeclarativeMetaType::ModuleApi> moduleApis; + bool sorted; + }; + typedef QHash<QByteArray, ModuleApiList> ModuleApis; + ModuleApis moduleApis; + int moduleApiCount; struct ModuleInfo { ModuleInfo(int major, int minor) @@ -119,6 +128,11 @@ struct QDeclarativeMetaTypeData Q_GLOBAL_STATIC(QDeclarativeMetaTypeData, metaTypeData) Q_GLOBAL_STATIC(QReadWriteLock, metaTypeDataLock) +QDeclarativeMetaTypeData::QDeclarativeMetaTypeData() +: moduleApiCount(0) +{ +} + QDeclarativeMetaTypeData::~QDeclarativeMetaTypeData() { for (int i = 0; i < types.count(); ++i) @@ -664,6 +678,34 @@ int registerType(const QDeclarativePrivate::RegisterType &type) return index; } +int registerModuleApi(const QDeclarativePrivate::RegisterModuleApi &api) +{ + QWriteLocker lock(metaTypeDataLock()); + + QDeclarativeMetaTypeData *data = metaTypeData(); + QByteArray uri(api.uri); + QDeclarativeMetaType::ModuleApi import; + import.major = api.versionMajor; + import.minor = api.versionMinor; + import.script = api.scriptApi; + import.qobject = api.qobjectApi; + + int index = data->moduleApiCount++; + + QDeclarativeMetaTypeData::ModuleApis::Iterator iter = data->moduleApis.find(uri); + if (iter == data->moduleApis.end()) { + QDeclarativeMetaTypeData::ModuleApiList apis; + apis.moduleApis << import; + data->moduleApis.insert(uri, apis); + } else { + iter->moduleApis << import; + iter->sorted = false; + } + + return index; +} + + /* This method is "over generalized" to allow us to (potentially) register more types of things in the future without adding exported symbols. @@ -676,13 +718,16 @@ int QDeclarativePrivate::qmlregister(RegistrationType type, void *data) return registerInterface(*reinterpret_cast<RegisterInterface *>(data)); } else if (type == AutoParentRegistration) { return registerAutoParentFunction(*reinterpret_cast<RegisterAutoParent *>(data)); + } else if (type == ModuleApiRegistration) { + return registerModuleApi(*reinterpret_cast<RegisterModuleApi *>(data)); } return -1; } /* - Have any types been registered for \a module with at least versionMajor.versionMinor, and types - for \a module with at most versionMajor.versionMinor. + Returns true if any type or API has been registered for the given \a module with at least + versionMajor.versionMinor, or if types have been registered for \a module with at most + versionMajor.versionMinor. So if only 4.7 and 4.9 have been registered, 4.7,4.8, and 4.9 are valid, but not 4.6 nor 4.10. @@ -691,13 +736,27 @@ int QDeclarativePrivate::qmlregister(RegistrationType type, void *data) bool QDeclarativeMetaType::isModule(const QByteArray &module, int versionMajor, int versionMinor) { QDeclarativeMetaTypeData *data = metaTypeData(); + + // first, check Types QDeclarativeMetaTypeData::ModuleInfoHash::Iterator it = data->modules.find(module); - return it != data->modules.end() + if (it != data->modules.end() && ((versionMajor<0 && versionMinor<0) || (((*it).vmajor_max > versionMajor || ((*it).vmajor_max == versionMajor && (*it).vminor_max >= versionMinor)) && ((*it).vmajor_min < versionMajor || - ((*it).vmajor_min == versionMajor && (*it).vminor_min <= versionMinor)))); + ((*it).vmajor_min == versionMajor && (*it).vminor_min <= versionMinor))))) { + return true; + } + + // then, check ModuleApis + foreach (const QDeclarativeMetaType::ModuleApi &mApi, data->moduleApis.value(module).moduleApis) { + if ((versionMajor<0 && versionMinor<0) + || (mApi.major == versionMajor && mApi.minor == versionMinor)) { + return true; + } + } + + return false; } QList<QDeclarativePrivate::AutoParentFunction> QDeclarativeMetaType::parentFunctions() @@ -707,6 +766,35 @@ QList<QDeclarativePrivate::AutoParentFunction> QDeclarativeMetaType::parentFunct return data->parentFunctions; } +static bool operator<(const QDeclarativeMetaType::ModuleApi &lhs, const QDeclarativeMetaType::ModuleApi &rhs) +{ + return lhs.major < rhs.major || (lhs.major == rhs.major && lhs.minor < rhs.minor); +} + +QDeclarativeMetaType::ModuleApi +QDeclarativeMetaType::moduleApi(const QByteArray &uri, int versionMajor, int versionMinor) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + QDeclarativeMetaTypeData::ModuleApis::Iterator iter = data->moduleApis.find(uri); + if (iter == data->moduleApis.end()) + return ModuleApi(); + + if (iter->sorted == false) { + qSort(iter->moduleApis.begin(), iter->moduleApis.end()); + iter->sorted = true; + } + + for (int ii = iter->moduleApis.count() - 1; ii >= 0; --ii) { + const ModuleApi &import = iter->moduleApis.at(ii); + if (import.major == versionMajor && import.minor <= versionMinor) + return import; + } + + return ModuleApi(); +} + QObject *QDeclarativeMetaType::toQObject(const QVariant &v, bool *ok) { if (!isQObject(v.userType())) { diff --git a/src/declarative/qml/qdeclarativemetatype_p.h b/src/declarative/qml/qdeclarativemetatype_p.h index aab1c31ef5..291bc38222 100644 --- a/src/declarative/qml/qdeclarativemetatype_p.h +++ b/src/declarative/qml/qdeclarativemetatype_p.h @@ -59,6 +59,7 @@ #include <QtCore/qvariant.h> #include <QtCore/qbitarray.h> #include <private/qdeclarativeglobal_p.h> +#include <QtScript/qscriptvalue.h> QT_BEGIN_NAMESPACE @@ -106,6 +107,25 @@ public: static bool isModule(const QByteArray &module, int versionMajor, int versionMinor); static QList<QDeclarativePrivate::AutoParentFunction> parentFunctions(); + + struct ModuleApiInstance { + ModuleApiInstance() + : scriptCallback(0), qobjectCallback(0), qobjectApi(0) {} + + QScriptValue (*scriptCallback)(QDeclarativeEngine *, QScriptEngine *); + QObject *(*qobjectCallback)(QDeclarativeEngine *, QScriptEngine *); + QScriptValue scriptApi; + QObject *qobjectApi; + }; + struct ModuleApi { + inline ModuleApi(); + inline bool operator==(const ModuleApi &) const; + int major; + int minor; + QScriptValue (*script)(QDeclarativeEngine *, QScriptEngine *); + QObject *(*qobject)(QDeclarativeEngine *, QScriptEngine *); + }; + static ModuleApi moduleApi(const QByteArray &, int, int); }; class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeType @@ -168,6 +188,25 @@ private: QDeclarativeTypePrivate *d; }; +QDeclarativeMetaType::ModuleApi::ModuleApi() +// : major(0), minor(0), script(0), qobject(0) +{ + major = 0; + minor = 0; + script = 0; + qobject = 0; +} + +bool QDeclarativeMetaType::ModuleApi::operator==(const ModuleApi &other) const +{ + return major == other.major && minor == other.minor && script == other.script && qobject == other.qobject; +} + +inline uint qHash(const QDeclarativeMetaType::ModuleApi &import) +{ + return import.major ^ import.minor ^ quintptr(import.script) ^ quintptr(import.qobject); +} + QT_END_NAMESPACE #endif // QDECLARATIVEMETATYPE_P_H diff --git a/src/declarative/qml/qdeclarativeobjectscriptclass.cpp b/src/declarative/qml/qdeclarativeobjectscriptclass.cpp index 9eecc65e3c..edc1755a72 100644 --- a/src/declarative/qml/qdeclarativeobjectscriptclass.cpp +++ b/src/declarative/qml/qdeclarativeobjectscriptclass.cpp @@ -403,6 +403,33 @@ void QDeclarativeObjectScriptClass::setProperty(QObject *obj, } else if (value.isFunction() && !value.isRegExp()) { // this is handled by the binding creation above } else { + //### expand optimization for other known types + if (lastData->propType == QMetaType::Int && value.isNumber()) { + int rawValue = qRound(value.toNumber()); + int status = -1; + int flags = 0; + void *a[] = { (void *)&rawValue, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, + lastData->coreIndex, a); + return; + } else if (lastData->propType == QMetaType::QReal && value.isNumber()) { + qreal rawValue = qreal(value.toNumber()); + int status = -1; + int flags = 0; + void *a[] = { (void *)&rawValue, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, + lastData->coreIndex, a); + return; + } else if (lastData->propType == QMetaType::QString && value.isString()) { + const QString &rawValue = value.toString(); + int status = -1; + int flags = 0; + void *a[] = { (void *)&rawValue, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, + lastData->coreIndex, a); + return; + } + QVariant v; if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >()); diff --git a/src/declarative/qml/qdeclarativeprivate.h b/src/declarative/qml/qdeclarativeprivate.h index 7ac3369fc7..fea9eae109 100644 --- a/src/declarative/qml/qdeclarativeprivate.h +++ b/src/declarative/qml/qdeclarativeprivate.h @@ -74,6 +74,9 @@ public: }; +class QScriptValue; +class QScriptEngine; +class QDeclarativeEngine; class QDeclarativeCustomParser; namespace QDeclarativePrivate { @@ -233,10 +236,22 @@ namespace QDeclarativePrivate AutoParentFunction function; }; + struct RegisterModuleApi { + int version; + + const char *uri; + int versionMajor; + int versionMinor; + + QScriptValue (*scriptApi)(QDeclarativeEngine *, QScriptEngine *); + QObject *(*qobjectApi)(QDeclarativeEngine *, QScriptEngine *); + }; + enum RegistrationType { TypeRegistration = 0, InterfaceRegistration = 1, - AutoParentRegistration = 2 + AutoParentRegistration = 2, + ModuleApiRegistration = 3, }; int Q_DECLARATIVE_EXPORT qmlregister(RegistrationType, void *); diff --git a/src/declarative/qml/qdeclarativepropertycache.cpp b/src/declarative/qml/qdeclarativepropertycache.cpp index 6a39a65532..9cbb4fa8cc 100644 --- a/src/declarative/qml/qdeclarativepropertycache.cpp +++ b/src/declarative/qml/qdeclarativepropertycache.cpp @@ -61,6 +61,8 @@ QDeclarativePropertyCache::Data::Flags QDeclarativePropertyCache::Data::flagsFor flags |= Data::IsWritable; if (p.isResettable()) flags |= Data::IsResettable; + if (p.isFinal()) + flags |= Data::IsFinal; if (propType == qMetaTypeId<QDeclarativeBinding *>()) { flags |= Data::IsQmlBinding; diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h index eeeff1aea1..65a8725b8f 100644 --- a/src/declarative/qml/qdeclarativepropertycache_p.h +++ b/src/declarative/qml/qdeclarativepropertycache_p.h @@ -84,20 +84,21 @@ public: IsWritable = 0x00000002, IsResettable = 0x00000004, IsAlias = 0x00000008, + IsFinal = 0x00000010, // These are mutualy exclusive - IsFunction = 0x00000010, - IsQObjectDerived = 0x00000020, - IsEnumType = 0x00000040, - IsQList = 0x00000080, - IsQmlBinding = 0x00000100, - IsQScriptValue = 0x00000200, + IsFunction = 0x00000020, + IsQObjectDerived = 0x00000040, + IsEnumType = 0x00000080, + IsQList = 0x00000100, + IsQmlBinding = 0x00000200, + IsQScriptValue = 0x00000400, // Apply only to IsFunctions - IsVMEFunction = 0x00000400, - HasArguments = 0x00000800, - IsSignal = 0x00001000, - IsVMESignal = 0x00002000 + IsVMEFunction = 0x00000800, + HasArguments = 0x00001000, + IsSignal = 0x00002000, + IsVMESignal = 0x00004000 }; Q_DECLARE_FLAGS(Flags, Flag) diff --git a/src/declarative/qml/qdeclarativescarceresourcescriptclass.cpp b/src/declarative/qml/qdeclarativescarceresourcescriptclass.cpp new file mode 100644 index 0000000000..121d0a1a35 --- /dev/null +++ b/src/declarative/qml/qdeclarativescarceresourcescriptclass.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativescarceresourcescriptclass_p.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativedata_p.h" +#include "private/qdeclarativetypenamescriptclass_p.h" +#include "private/qdeclarativelistscriptclass_p.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativeguard_p.h" +#include "private/qdeclarativevmemetaobject_p.h" + +#include <QtCore/qtimer.h> +#include <QtCore/qvarlengtharray.h> +#include <QtScript/qscriptcontextinfo.h> + +Q_DECLARE_METATYPE(QScriptValue); + +QT_BEGIN_NAMESPACE + +QDeclarativeScarceResourceScriptClass::QDeclarativeScarceResourceScriptClass(QDeclarativeEngine *bindEngine) + : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), engine(bindEngine) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + // Properties of this type can be explicitly preserved by clients, + // which prevents the scarce resource from being automatically + // released after the binding has been evaluated. + m_preserve = scriptEngine->newFunction(preserve); + m_preserveId = createPersistentIdentifier(QLatin1String("preserve")); + + // Similarly, they can be explicitly destroyed by clients, + // which releases the scarce resource. + m_destroy = scriptEngine->newFunction(destroy); + m_destroyId = createPersistentIdentifier(QLatin1String("destroy")); +} + +QDeclarativeScarceResourceScriptClass::~QDeclarativeScarceResourceScriptClass() +{ +} + +/* + Returns a JavaScript object whose instance data is a new scarce resource data. + The scarce resource is added to the doubly-linked-list of scarce resources in the engine + so that the scarce resource can be released after evaluation completes. + */ +QScriptValue QDeclarativeScarceResourceScriptClass::newScarceResource(const QVariant &v) +{ + // create the scarce resource + ScarceResourceData *srd = new ScarceResourceData(v); + + // insert into the linked list + QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine); + srd->insertInto(&enginePrivate->scarceResources); + Q_ASSERT(enginePrivate->scarceResourcesRefCount > 0); + + // return the javascript object with the scarce resource instance data + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + return QScriptDeclarativeClass::newObject(scriptEngine, this, srd); // JSC takes ownership of srd. +} + +QVariant QDeclarativeScarceResourceScriptClass::toVariant(Object *object, bool *ok) +{ + ScarceResourceData *obj = static_cast<ScarceResourceData*>(object); + if (ok) *ok = true; + return obj->resource; +} + +QVariant QDeclarativeScarceResourceScriptClass::toVariant(const QScriptValue &value) +{ + Q_ASSERT(scriptClass(value) == this); + + return toVariant(object(value), 0); +} + +// The destroy() and preserve() function properties are readable. +QScriptClass::QueryFlags +QDeclarativeScarceResourceScriptClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(object) + Q_UNUSED(flags) + + if (name == m_destroyId.identifier || name == m_preserveId.identifier) + return (QScriptClass::HandlesReadAccess); + return 0; +} + +// Return the (function) values which may be evaluated by clients. +QDeclarativeScarceResourceScriptClass::Value +QDeclarativeScarceResourceScriptClass::property(Object *object, const Identifier &name) +{ + Q_UNUSED(object) + + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + // functions + if (name == m_preserveId.identifier) + return Value(scriptEngine, m_preserve); + else if (name == m_destroyId.identifier) + return Value(scriptEngine, m_destroy); + + return Value(); +} + +/* + The user explicitly wants to preserve the resource. + We remove the scarce resource from the engine's linked list + of resources to release after evaluation completes. + */ +QScriptValue QDeclarativeScarceResourceScriptClass::preserve(QScriptContext *context, QScriptEngine *engine) +{ + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); + QScriptValue that = context->thisObject(); + + if (scriptClass(that) != p->scarceResourceClass) + return engine->undefinedValue(); + + // The client wishes to preserve the resource in this SRD. + ScarceResourceData *data = static_cast<ScarceResourceData *>(p->scarceResourceClass->object(that)); + if (!data) + return engine->undefinedValue(); + + // remove node from list, without releasing the resource. + data->removeNode(); + + return engine->undefinedValue(); +} + +/* + The user explicitly wants to release the resource. + We set the internal scarce resource variant to the invalid variant. + */ +QScriptValue QDeclarativeScarceResourceScriptClass::destroy(QScriptContext *context, QScriptEngine *engine) +{ + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); + QScriptValue that = context->thisObject(); + + if (scriptClass(that) != p->scarceResourceClass) + return engine->undefinedValue(); + + // the client wishes to release the resource in this SRD. + ScarceResourceData *data = static_cast<ScarceResourceData *>(p->scarceResourceClass->object(that)); + if (!data) + return engine->undefinedValue(); + + // release the resource and remove the node from the list. + data->releaseResource(); + + return engine->undefinedValue(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativescarceresourcescriptclass_p.h b/src/declarative/qml/qdeclarativescarceresourcescriptclass_p.h new file mode 100644 index 0000000000..2a1390a230 --- /dev/null +++ b/src/declarative/qml/qdeclarativescarceresourcescriptclass_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESCARCERESOURCESCRIPTCLASS_P_H +#define QDECLARATIVESCARCERESOURCESCRIPTCLASS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qdeclarativepropertycache_p.h" +#include "private/qdeclarativetypenamecache_p.h" + +#include <private/qscriptdeclarativeclass_p.h> +#include <QtScript/qscriptengine.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; + +/* + Scarce resources (like pixmaps and textures) are managed manually + in that the variant will be set to the invalid variant once the + JavaScript engine has finished using the JavaScript object whose + instance data is the ScarceResourceData (but before the garbage + collector frees the JavaScript object itself). + + The engine stores a doubly-linked-list of scarce resources which + will to be cleaned up after a binding is successfully evaluated + (unless the user explicitly preserves the scarce resource). + + A ScarceResourceData pointer should not be deleted manually, as + all instances of a ScarceResourceData should be owned by the + JavaScript engine. + */ +struct ScarceResourceData : public QScriptDeclarativeClass::Object { + ScarceResourceData(const QVariant &v) : resource(v), prev(0), next(0) + { + } + + virtual ~ScarceResourceData() + { + releaseResource(); + } + + // Insert this resource into the given list of resources. + void insertInto(ScarceResourceData **list) + { + // This node becomes the head of the list. + next = *list; // so our next = old list head + *list = this; // list now points to us (we're the head) + prev = list; // as we're the head, our prev ptr becomes the list ptr. + + // and the next node's prev pointer must contain a ptr to our next ptr, + // since per definition, prev always contains a pointer to the previous node's "next" ptr, + // and the "this" node is the "this->next" node's "prev" node. + if (next) next->prev = &next; + } + + // Remove this resource from the list of resources, without releasing the resource. + void removeNode() + { + // whatever previously pointed to this node (ie, as that node's "next" node) + // should now point to our next node (since we no longer exist in the list). + // and the next node's prev ptr should point to our prev node. + if (prev) *prev = next; + if (next) next->prev = prev; + prev = 0; + next = 0; + } + + // Release this resource, and remove from the list. + void releaseResource() + { + resource = QVariant(); + removeNode(); + } + + QVariant resource; + + // prev always contains a pointer to the previous node's "next" ptr. + // :. for the head node, [*prev] will be engine->scarceResources + // :. for every other node, [*prev] will be the previous node's "next" ptr. + ScarceResourceData **prev; + ScarceResourceData *next; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeScarceResourceScriptClass : public QScriptDeclarativeClass +{ +public: + QDeclarativeScarceResourceScriptClass(QDeclarativeEngine *); + ~QDeclarativeScarceResourceScriptClass(); + + // Creates a new JavaScript object whose instance data is the scarce resource v + QScriptValue newScarceResource(const QVariant &v); + + // inherited from QScriptDeclarativeClass + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + virtual Value property(Object *, const Identifier &); + virtual QVariant toVariant(Object *, bool *ok = 0); + QVariant toVariant(const QScriptValue &value); + +private: + PersistentIdentifier m_preserveId; + PersistentIdentifier m_destroyId; + QScriptValue m_preserve; + QScriptValue m_destroy; + + static QScriptValue preserve(QScriptContext *context, QScriptEngine *engine); + static QScriptValue destroy(QScriptContext *context, QScriptEngine *engine); + + QDeclarativeEngine *engine; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESCARCERESOURCESCRIPTCLASS_P_H diff --git a/src/declarative/qml/qdeclarativescriptparser.cpp b/src/declarative/qml/qdeclarativescriptparser.cpp index e04cfc52af..352e341437 100644 --- a/src/declarative/qml/qdeclarativescriptparser.cpp +++ b/src/declarative/qml/qdeclarativescriptparser.cpp @@ -59,6 +59,22 @@ QT_BEGIN_NAMESPACE using namespace QDeclarativeJS; using namespace QDeclarativeParser; +void QDeclarativeScriptParser::Import::extractVersion(int *maj, int *min) const +{ + *maj = -1; *min = -1; + + if (!version.isEmpty()) { + int dot = version.indexOf(QLatin1Char('.')); + if (dot < 0) { + *maj = version.toInt(); + *min = 0; + } else { + *maj = version.left(dot).toInt(); + *min = version.mid(dot+1).toInt(); + } + } +} + namespace { class ProcessAST: protected AST::Visitor @@ -896,6 +912,19 @@ static void replaceWithSpace(QString &str, int idx, int n) *data++ = space; } +static QDeclarativeParser::LocationSpan +locationFromLexer(const QDeclarativeJS::Lexer &lex, int startLine, int startColumn, int startOffset) +{ + QDeclarativeParser::LocationSpan l; + + l.start.line = startLine; l.start.column = startColumn; + l.end.line = lex.endLineNo(); l.end.column = lex.endColumnNo(); + l.range.offset = startOffset; + l.range.length = lex.tokenOffset() + lex.tokenLength() - startOffset; + + return l; +} + /* Searches for ".pragma <value>" declarations within \a script. Currently supported pragmas are: @@ -1024,7 +1053,8 @@ QDeclarativeScriptParser::JavaScriptMetaData QDeclarativeScriptParser::extractMe return rv; int startOffset = l.tokenOffset(); - int startLine = l.currentLineNo(); + int startLine = l.startLineNo(); + int startColumn = l.startColumnNo(); token = l.lex(); @@ -1062,8 +1092,11 @@ QDeclarativeScriptParser::JavaScriptMetaData QDeclarativeScriptParser::extractMe if (!importId.at(0).isUpper()) return rv; + QDeclarativeParser::LocationSpan location = + locationFromLexer(l, startLine, startColumn, startOffset); + token = l.lex(); - if (l.currentLineNo() == startLine) + if (l.startLineNo() == startLine) return rv; replaceWithSpace(script, startOffset, endOffset - startOffset); @@ -1072,9 +1105,9 @@ QDeclarativeScriptParser::JavaScriptMetaData QDeclarativeScriptParser::extractMe import.type = Import::Script; import.uri = file; import.qualifier = importId; + import.location = location; rv.imports << import; - } else { // URI QString uri; @@ -1117,8 +1150,11 @@ QDeclarativeScriptParser::JavaScriptMetaData QDeclarativeScriptParser::extractMe if (!importId.at(0).isUpper()) return rv; + QDeclarativeParser::LocationSpan location = + locationFromLexer(l, startLine, startColumn, startOffset); + token = l.lex(); - if (l.currentLineNo() == startLine) + if (l.startLineNo() == startLine) return rv; replaceWithSpace(script, startOffset, endOffset - startOffset); @@ -1128,6 +1164,7 @@ QDeclarativeScriptParser::JavaScriptMetaData QDeclarativeScriptParser::extractMe import.uri = uri; import.version = version; import.qualifier = importId; + import.location = location; rv.imports << import; } @@ -1143,7 +1180,7 @@ QDeclarativeScriptParser::JavaScriptMetaData QDeclarativeScriptParser::extractMe QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength()); int endOffset = l.tokenLength() + l.tokenOffset(); - if (pragmaValue == QLatin1String("library")) { + if (pragmaValue == library) { pragmas |= QDeclarativeParser::Object::ScriptBlock::Shared; replaceWithSpace(script, startOffset, endOffset - startOffset); } else { diff --git a/src/declarative/qml/qdeclarativescriptparser_p.h b/src/declarative/qml/qdeclarativescriptparser_p.h index e5e0d7075d..fd89f757cc 100644 --- a/src/declarative/qml/qdeclarativescriptparser_p.h +++ b/src/declarative/qml/qdeclarativescriptparser_p.h @@ -82,6 +82,8 @@ public: QString qualifier; QString version; + void extractVersion(int *maj, int *min) const; + QDeclarativeParser::LocationSpan location; }; diff --git a/src/declarative/qml/qdeclarativetypeloader.cpp b/src/declarative/qml/qdeclarativetypeloader.cpp index 26f3996871..1c1eeee2cf 100644 --- a/src/declarative/qml/qdeclarativetypeloader.cpp +++ b/src/declarative/qml/qdeclarativetypeloader.cpp @@ -607,7 +607,7 @@ void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArra if (!blob->isError() && !blob->isWaiting()) blob->allDependenciesDone(); - if (blob->status() != QDeclarativeDataBlob::Error) + if (blob->status() != QDeclarativeDataBlob::Error) blob->m_status = QDeclarativeDataBlob::WaitingForDependencies; blob->m_inCallback = false; @@ -674,24 +674,23 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const } /*! -Return a QDeclarativeScriptData for \a url. The QDeclarativeScriptData may be cached. +Return a QDeclarativeScriptBlob for \a url. The QDeclarativeScriptData may be cached. */ -QDeclarativeScriptData *QDeclarativeTypeLoader::getScript(const QUrl &url) +QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url) { Q_ASSERT(!url.isRelative() && (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url)))); - QDeclarativeScriptData *scriptData = m_scriptCache.value(url); + QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url); - if (!scriptData) { - scriptData = new QDeclarativeScriptData(url); - m_scriptCache.insert(url, scriptData); - QDeclarativeDataLoader::load(scriptData); + if (!scriptBlob) { + scriptBlob = new QDeclarativeScriptBlob(url, this); + m_scriptCache.insert(url, scriptBlob); + QDeclarativeDataLoader::load(scriptBlob); } - scriptData->addref(); - return scriptData; + return scriptBlob; } /*! @@ -868,13 +867,14 @@ void QDeclarativeTypeData::dataReceived(const QByteArray &data) } } else if (import.type == QDeclarativeScriptParser::Import::Script) { QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri)); - QDeclarativeScriptData *data = typeLoader()->getScript(scriptUrl); - addDependency(data); + QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl); + addDependency(blob); ScriptReference ref; ref.location = import.location.start; ref.qualifier = import.qualifier; - ref.script = data; + ref.script = blob; + blob->addref(); m_scripts << ref; } @@ -933,14 +933,31 @@ void QDeclarativeTypeData::resolveTypes() // For local urls, add an implicit import "." as first (most overridden) lookup. // This will also trigger the loading of the qmldir and the import of any native // types from available plugins. + QList<QDeclarativeError> errors; if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) { m_imports.addImport(importDatabase, QLatin1String("."), QString(), -1, -1, QDeclarativeScriptParser::Import::File, - qmldir->dirComponents(), 0); + qmldir->dirComponents(), &errors); } else { m_imports.addImport(importDatabase, QLatin1String("."), QString(), -1, -1, QDeclarativeScriptParser::Import::File, - QDeclarativeDirComponents(), 0); + QDeclarativeDirComponents(), &errors); + } + + // remove any errors which are due to the implicit import which aren't real errors. + // for example, if the implicitly included qmldir file doesn't exist, that is not an error. + QList<QDeclarativeError> realErrors; + for (int i = 0; i < errors.size(); ++i) { + if (errors.at(i).description() != QDeclarativeImportDatabase::tr("import \".\" has no qmldir and no namespace") + && errors.at(i).description() != QDeclarativeImportDatabase::tr("\".\": no such directory")) { + realErrors.prepend(errors.at(i)); // this is a real error. + } + } + + // report any real errors which occurred during plugin loading or qmldir parsing. + if (!realErrors.isEmpty()) { + setError(realErrors); + return; } foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) { @@ -950,34 +967,31 @@ void QDeclarativeTypeData::resolveTypes() if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) { QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir"))); - if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl)) + if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl)) qmldircomponentsnetwork = qmldir->dirComponents(); } int vmaj = -1; int vmin = -1; + import.extractVersion(&vmaj, &vmin); - if (!import.version.isEmpty()) { - int dot = import.version.indexOf(QLatin1Char('.')); - if (dot < 0) { - vmaj = import.version.toInt(); - vmin = 0; - } else { - vmaj = import.version.left(dot).toInt(); - vmin = import.version.mid(dot+1).toInt(); - } - } - - QString errorString; + QList<QDeclarativeError> errors; if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, - vmaj, vmin, import.type, qmldircomponentsnetwork, &errorString)) { + vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) { QDeclarativeError error; + if (errors.size()) { + error = errors.takeFirst(); + } else { + // this should not be possible! + // Description should come from error provided by addImport() function. + error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database")); + } error.setUrl(m_imports.baseUrl()); - error.setDescription(errorString); error.setLine(import.location.start.line); error.setColumn(import.location.start.column); + errors.prepend(error); // put it back on the list after filling out information. - setError(error); + setError(errors); return; } } @@ -991,29 +1005,38 @@ void QDeclarativeTypeData::resolveTypes() int majorVersion; int minorVersion; QDeclarativeImportedNamespace *typeNamespace = 0; - QString errorString; + QList<QDeclarativeError> errors; if (!m_imports.resolveType(typeName, &ref.type, &url, &majorVersion, &minorVersion, - &typeNamespace, &errorString) || typeNamespace) { + &typeNamespace, &errors) || typeNamespace) { // Known to not be a type: // - known to be a namespace (Namespace {}) // - type with unknown namespace (UnknownNamespace.SomeType {}) QDeclarativeError error; - error.setUrl(m_imports.baseUrl()); QString userTypeName = parserRef->name; userTypeName.replace(QLatin1Char('/'),QLatin1Char('.')); - if (typeNamespace) + if (typeNamespace) { error.setDescription(QDeclarativeTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName)); - else - error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(errorString)); + } else { + if (errors.size()) { + error = errors.takeFirst(); + } else { + // this should not be possible! + // Description should come from error provided by addImport() function. + error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database")); + } + error.setUrl(m_imports.baseUrl()); + error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(error.description())); + } if (!parserRef->refObjects.isEmpty()) { QDeclarativeParser::Object *obj = parserRef->refObjects.first(); error.setLine(obj->location.start.line); error.setColumn(obj->location.start.column); } - - setError(error); + + errors.prepend(error); + setError(errors); return; } @@ -1046,25 +1069,156 @@ QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url) return 0; } -QDeclarativeScriptData::QDeclarativeScriptData(const QUrl &url) -: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None) +QDeclarativeScriptData::QDeclarativeScriptData(QDeclarativeEngine *engine) +: QDeclarativeCleanup(engine), importCache(0), pragmas(QDeclarativeParser::Object::ScriptBlock::None), + m_loaded(false) +{ +} + +QDeclarativeScriptData::~QDeclarativeScriptData() { + clear(); } -QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptData::pragmas() const +void QDeclarativeScriptData::clear() +{ + if (importCache) { + importCache->release(); + importCache = 0; + } + + for (int ii = 0; ii < scripts.count(); ++ii) + scripts.at(ii)->release(); + scripts.clear(); +} + +QDeclarativeScriptBlob::QDeclarativeScriptBlob(const QUrl &url, QDeclarativeTypeLoader *loader) +: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None), + m_scriptData(0), m_typeLoader(loader) +{ +} + +QDeclarativeScriptBlob::~QDeclarativeScriptBlob() +{ + if (m_scriptData) { + m_scriptData->release(); + m_scriptData = 0; + } +} + +QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const { return m_pragmas; } -QString QDeclarativeScriptData::scriptSource() const +QString QDeclarativeScriptBlob::scriptSource() const { return m_source; } -void QDeclarativeScriptData::dataReceived(const QByteArray &data) +QDeclarativeTypeLoader *QDeclarativeScriptBlob::typeLoader() const +{ + return m_typeLoader; +} + +const QDeclarativeImports &QDeclarativeScriptBlob::imports() const +{ + return m_imports; +} + +QDeclarativeScriptData *QDeclarativeScriptBlob::scriptData() const +{ + return m_scriptData; +} + +void QDeclarativeScriptBlob::dataReceived(const QByteArray &data) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine()); + QDeclarativeImportDatabase *importDatabase = &ep->importDatabase; + m_source = QString::fromUtf8(data); - m_pragmas = QDeclarativeScriptParser::extractPragmas(m_source); + + QDeclarativeScriptParser::JavaScriptMetaData metadata = + QDeclarativeScriptParser::extractMetaData(m_source); + + m_imports.setBaseUrl(finalUrl()); + + m_pragmas = metadata.pragmas; + + foreach (const QDeclarativeScriptParser::Import &import, metadata.imports) { + Q_ASSERT(import.type != QDeclarativeScriptParser::Import::File); + + if (import.type == QDeclarativeScriptParser::Import::Script) { + QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri)); + QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl); + addDependency(blob); + + ScriptReference ref; + ref.location = import.location.start; + ref.qualifier = import.qualifier; + ref.script = blob; + blob->addref(); + m_scripts << ref; + } else { + Q_ASSERT(import.type == QDeclarativeScriptParser::Import::Library); + int vmaj = -1; + int vmin = -1; + import.extractVersion(&vmaj, &vmin); + + QList<QDeclarativeError> errors; + if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin, + import.type, QDeclarativeDirComponents(), &errors)) { + QDeclarativeError error = errors.takeFirst(); + // description should be set by addImport(). + error.setUrl(m_imports.baseUrl()); + error.setLine(import.location.start.line); + error.setColumn(import.location.start.column); + errors.prepend(error); + + setError(errors); + return; + } + } + } +} + +void QDeclarativeScriptBlob::done() +{ + // Check all script dependencies for errors + for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { + const ScriptReference &script = m_scripts.at(ii); + Q_ASSERT(script.script->isCompleteOrError()); + if (script.script->isError()) { + QList<QDeclarativeError> errors = script.script->errors(); + QDeclarativeError error; + error.setUrl(finalUrl()); + error.setLine(script.location.line); + error.setColumn(script.location.column); + error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString())); + errors.prepend(error); + setError(errors); + } + } + + if (isError()) + return; + + QDeclarativeEngine *engine = typeLoader()->engine(); + m_scriptData = new QDeclarativeScriptData(engine); + m_scriptData->url = finalUrl(); + m_scriptData->importCache = new QDeclarativeTypeNameCache(engine); + + for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { + const ScriptReference &script = m_scripts.at(ii); + + m_scriptData->scripts.append(script.script); + m_scriptData->importCache->add(script.qualifier, ii); + } + + m_imports.populateCache(m_scriptData->importCache, engine); + + m_scriptData->pragmas = m_pragmas; + m_scriptData->m_program = QScriptProgram(m_source, finalUrl().toString()); } QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url) diff --git a/src/declarative/qml/qdeclarativetypeloader_p.h b/src/declarative/qml/qdeclarativetypeloader_p.h index 7f487a0e24..1c0798d67b 100644 --- a/src/declarative/qml/qdeclarativetypeloader_p.h +++ b/src/declarative/qml/qdeclarativetypeloader_p.h @@ -55,8 +55,11 @@ #include <QtCore/qobject.h> #include <QtNetwork/qnetworkreply.h> +#include <QtScript/qscriptvalue.h> +#include <QtScript/qscriptprogram.h> #include <QtDeclarative/qdeclarativeerror.h> #include <QtDeclarative/qdeclarativeengine.h> +#include <private/qdeclarativecleanup_p.h> #include <private/qdeclarativescriptparser_p.h> #include <private/qdeclarativedirparser_p.h> #include <private/qdeclarativeimport_p.h> @@ -64,6 +67,7 @@ QT_BEGIN_NAMESPACE class QDeclarativeScriptData; +class QDeclarativeScriptBlob; class QDeclarativeQmldirData; class QDeclarativeTypeLoader; class QDeclarativeCompiledData; @@ -140,7 +144,7 @@ private: QUrl m_finalUrl; // List of QDeclarativeDataBlob's that are waiting for me to complete. - QList<QDeclarativeDataBlob *> m_waitingOnMe; + QList<QDeclarativeDataBlob *> m_waitingOnMe; // List of QDeclarativeDataBlob's that I am waiting for to complete. QList<QDeclarativeDataBlob *> m_waitingFor; @@ -178,7 +182,6 @@ private: NetworkReplies m_networkReplies; }; - class Q_AUTOTEST_EXPORT QDeclarativeTypeLoader : public QDeclarativeDataLoader { Q_OBJECT @@ -196,11 +199,11 @@ public: QDeclarativeTypeData *get(const QByteArray &, const QUrl &url, Options = None); void clearCache(); - QDeclarativeScriptData *getScript(const QUrl &); + QDeclarativeScriptBlob *getScript(const QUrl &); QDeclarativeQmldirData *getQmldir(const QUrl &); private: typedef QHash<QUrl, QDeclarativeTypeData *> TypeCache; - typedef QHash<QUrl, QDeclarativeScriptData *> ScriptCache; + typedef QHash<QUrl, QDeclarativeScriptBlob *> ScriptCache; typedef QHash<QUrl, QDeclarativeQmldirData *> QmldirCache; TypeCache m_typeCache; @@ -230,7 +233,7 @@ public: QDeclarativeParser::Location location; QString qualifier; - QDeclarativeScriptData *script; + QDeclarativeScriptBlob *script; }; QDeclarativeTypeData(const QUrl &, QDeclarativeTypeLoader::Options, QDeclarativeTypeLoader *); @@ -285,20 +288,65 @@ private: QDeclarativeTypeLoader *m_typeLoader; }; -class Q_AUTOTEST_EXPORT QDeclarativeScriptData : public QDeclarativeDataBlob +class Q_AUTOTEST_EXPORT QDeclarativeScriptData : public QDeclarativeRefCount, public QDeclarativeCleanup { public: - QDeclarativeScriptData(const QUrl &); + QDeclarativeScriptData(QDeclarativeEngine *); + ~QDeclarativeScriptData(); + + QUrl url; + QDeclarativeTypeNameCache *importCache; + QList<QDeclarativeScriptBlob *> scripts; + QDeclarativeParser::Object::ScriptBlock::Pragmas pragmas; + +protected: + virtual void clear(); // From QDeclarativeCleanup + +private: + friend class QDeclarativeVME; + friend class QDeclarativeScriptBlob; + + bool m_loaded; + QScriptProgram m_program; + QScriptValue m_value; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeScriptBlob : public QDeclarativeDataBlob +{ +public: + QDeclarativeScriptBlob(const QUrl &, QDeclarativeTypeLoader *); + ~QDeclarativeScriptBlob(); + + struct ScriptReference + { + ScriptReference() : script(0) {} + + QDeclarativeParser::Location location; + QString qualifier; + QDeclarativeScriptBlob *script; + }; QDeclarativeParser::Object::ScriptBlock::Pragmas pragmas() const; QString scriptSource() const; + QDeclarativeTypeLoader *typeLoader() const; + const QDeclarativeImports &imports() const; + + QDeclarativeScriptData *scriptData() const; + protected: virtual void dataReceived(const QByteArray &); + virtual void done(); private: QDeclarativeParser::Object::ScriptBlock::Pragmas m_pragmas; QString m_source; + + QDeclarativeImports m_imports; + QList<ScriptReference> m_scripts; + QDeclarativeScriptData *m_scriptData; + + QDeclarativeTypeLoader *m_typeLoader; }; class Q_AUTOTEST_EXPORT QDeclarativeQmldirData : public QDeclarativeDataBlob diff --git a/src/declarative/qml/qdeclarativetypenamecache.cpp b/src/declarative/qml/qdeclarativetypenamecache.cpp index 48c72a7fef..b9577c17fa 100644 --- a/src/declarative/qml/qdeclarativetypenamecache.cpp +++ b/src/declarative/qml/qdeclarativetypenamecache.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE QDeclarativeTypeNameCache::QDeclarativeTypeNameCache(QDeclarativeEngine *e) -: QDeclarativeCleanup(e), engine(e) +: QDeclarativeCleanup(e), engine(e), m_moduleApi(0) { } @@ -60,6 +60,7 @@ void QDeclarativeTypeNameCache::clear() qDeleteAll(stringCache); stringCache.clear(); identifierCache.clear(); + m_moduleApi = 0; engine = 0; } @@ -114,5 +115,10 @@ QDeclarativeTypeNameCache::Data *QDeclarativeTypeNameCache::data(const QString & return stringCache.value(id); } +void QDeclarativeTypeNameCache::setModuleApi(QDeclarativeMetaType::ModuleApiInstance *api) +{ + m_moduleApi = api; +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativetypenamecache_p.h b/src/declarative/qml/qdeclarativetypenamecache_p.h index f2562da777..bcca41f4d5 100644 --- a/src/declarative/qml/qdeclarativetypenamecache_p.h +++ b/src/declarative/qml/qdeclarativetypenamecache_p.h @@ -55,6 +55,7 @@ #include "private/qdeclarativerefcount_p.h" #include "private/qdeclarativecleanup_p.h" +#include "private/qdeclarativemetatype_p.h" #include <private/qscriptdeclarativeclass_p.h> @@ -82,6 +83,10 @@ public: Data *data(const QString &) const; inline Data *data(const QScriptDeclarativeClass::Identifier &id) const; + inline bool isEmpty() const; + + inline QDeclarativeMetaType::ModuleApiInstance *moduleApi() const; + void setModuleApi(QDeclarativeMetaType::ModuleApiInstance *); protected: virtual void clear(); @@ -96,6 +101,7 @@ private: StringCache stringCache; IdentifierCache identifierCache; QDeclarativeEngine *engine; + QDeclarativeMetaType::ModuleApiInstance *m_moduleApi; }; QDeclarativeTypeNameCache::Data::Data() @@ -113,6 +119,16 @@ QDeclarativeTypeNameCache::Data *QDeclarativeTypeNameCache::data(const QScriptDe return identifierCache.value(id); } +bool QDeclarativeTypeNameCache::isEmpty() const +{ + return identifierCache.isEmpty(); +} + +QDeclarativeMetaType::ModuleApiInstance *QDeclarativeTypeNameCache::moduleApi() const +{ + return m_moduleApi; +} + QT_END_NAMESPACE #endif // QDECLARATIVETYPENAMECACHE_P_H diff --git a/src/declarative/qml/qdeclarativetypenamescriptclass.cpp b/src/declarative/qml/qdeclarativetypenamescriptclass.cpp index a7c0b2cfbe..d628b7065a 100644 --- a/src/declarative/qml/qdeclarativetypenamescriptclass.cpp +++ b/src/declarative/qml/qdeclarativetypenamescriptclass.cpp @@ -63,7 +63,7 @@ struct TypeNameData : public QScriptDeclarativeClass::Object { QDeclarativeTypeNameScriptClass::QDeclarativeTypeNameScriptClass(QDeclarativeEngine *bindEngine) : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), - engine(bindEngine), object(0), type(0) + engine(bindEngine), object(0), type(0), api(0) { } @@ -95,14 +95,35 @@ QDeclarativeTypeNameScriptClass::queryProperty(Object *obj, const Identifier &na object = 0; type = 0; + api = 0; QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); if (data->typeNamespace) { - QDeclarativeTypeNameCache::Data *d = data->typeNamespace->data(name); if (d && d->type) { type = d->type; return QScriptClass::HandlesReadAccess; + } else if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = data->typeNamespace->moduleApi()) { + if (moduleApi->scriptCallback) { + moduleApi->scriptApi = moduleApi->scriptCallback(engine, &ep->scriptEngine); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } else if (moduleApi->qobjectCallback) { + moduleApi->qobjectApi = moduleApi->qobjectCallback(engine, &ep->scriptEngine); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } + + api = moduleApi; + if (api->qobjectApi) { + return ep->objectClass->queryProperty(api->qobjectApi, name, flags, 0, + QDeclarativeObjectScriptClass::SkipAttachedProperties); + } else { + return QScriptClass::HandlesReadAccess; + } + + return 0; + } else { return 0; } @@ -147,6 +168,10 @@ QDeclarativeTypeNameScriptClass::property(Object *obj, const Identifier &name) return Value(scriptEngine, newObject(((TypeNameData *)obj)->object, type, ((TypeNameData *)obj)->mode)); } else if (object) { return ep->objectClass->property(object, name); + } else if (api && api->qobjectApi) { + return ep->objectClass->property(api->qobjectApi, name); + } else if (api) { + return propertyValue(api->scriptApi, name); } else { return Value(scriptEngine, enumValue); } @@ -154,11 +179,16 @@ QDeclarativeTypeNameScriptClass::property(Object *obj, const Identifier &name) void QDeclarativeTypeNameScriptClass::setProperty(Object *, const Identifier &n, const QScriptValue &v) { - Q_ASSERT(object); Q_ASSERT(!type); QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - ep->objectClass->setProperty(object, n, v, context()); + if (api) { + Q_ASSERT(api->qobjectApi); + ep->objectClass->setProperty(api->qobjectApi, n, v, context()); + } else { + Q_ASSERT(object); + ep->objectClass->setProperty(object, n, v, context()); + } } QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativetypenamescriptclass_p.h b/src/declarative/qml/qdeclarativetypenamescriptclass_p.h index cf7dbc8480..49e1ae809e 100644 --- a/src/declarative/qml/qdeclarativetypenamescriptclass_p.h +++ b/src/declarative/qml/qdeclarativetypenamescriptclass_p.h @@ -83,6 +83,7 @@ private: QDeclarativeEngine *engine; QObject *object; QDeclarativeType *type; + QDeclarativeMetaType::ModuleApiInstance *api; quint32 enumValue; }; diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index 781e1b8ea3..6bbc47bcaf 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -57,8 +57,9 @@ #include "private/qdeclarativevmemetaobject_p.h" #include "private/qdeclarativebinding_p_p.h" #include "private/qdeclarativecontext_p.h" -#include "private/qdeclarativecompiledbindings_p.h" +#include "private/qdeclarativev4bindings_p.h" #include "private/qdeclarativeglobal_p.h" +#include "private/qdeclarativeglobalscriptclass_p.h" #include "qdeclarativescriptstring.h" #include <QStack> @@ -71,6 +72,7 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qdatetime.h> +#include <QtScript/qscriptvalue.h> QT_BEGIN_NAMESPACE @@ -158,7 +160,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, const QList<int> &intData = comp->intData; const QList<float> &floatData = comp->floatData; const QList<QDeclarativePropertyCache *> &propertyCaches = comp->propertyCaches; - const QList<QDeclarativeParser::Object::ScriptBlock> &scripts = comp->scripts; + const QList<QDeclarativeScriptData *> &scripts = comp->scripts; const QList<QUrl> &urls = comp->urls; QDeclarativeEnginePrivate::SimpleList<QDeclarativeAbstractBinding> bindValues; @@ -186,7 +188,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, if (instr.init.contextCache != -1) ctxt->setIdPropertyData(comp->contextCaches.at(instr.init.contextCache)); if (instr.init.compiledBinding != -1) - ctxt->optimizedBindings = new QDeclarativeCompiledBindings(datas.at(instr.init.compiledBinding).constData(), ctxt); + ctxt->optimizedBindings = new QDeclarativeV4Bindings(datas.at(instr.init.compiledBinding).constData(), ctxt); } break; @@ -409,6 +411,15 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, } break; + case QDeclarativeInstruction::StoreByteArray: + { + QObject *target = stack.top(); + void *a[] = { (void *)&datas.at(instr.storeByteArray.value), 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeByteArray.propertyIndex, a); + } + break; + case QDeclarativeInstruction::StoreUrl: { QObject *target = stack.top(); @@ -701,7 +712,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreImportedScript: { - ctxt->addImportedScript(scripts.at(instr.storeScript.value)); + ctxt->importedScripts << run(ctxt, scripts.at(instr.storeScript.value)); } break; @@ -1054,5 +1065,68 @@ QDeclarativeCompiledData::TypeReference::createInstance(QDeclarativeContextData } } +QScriptValue QDeclarativeVME::run(QDeclarativeContextData *parentCtxt, QDeclarativeScriptData *script) +{ + if (script->m_loaded) + return script->m_value; + + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(parentCtxt->engine); + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(parentCtxt->engine); + + bool shared = script->pragmas & QDeclarativeParser::Object::ScriptBlock::Shared; + + // Create the script context if required + QDeclarativeContextData *ctxt = 0; + if (!shared) { + ctxt = new QDeclarativeContextData; + ctxt->isInternal = true; + ctxt->url = script->url; + + // For backward compatibility, if there are no imports, we need to use the + // imports from the parent context. See QTBUG-17518. + if (!script->importCache->isEmpty()) { + ctxt->imports = script->importCache; + } else { + ctxt->imports = parentCtxt->imports; + } + + if (ctxt->imports) { + ctxt->imports->addref(); + } + + ctxt->setParent(parentCtxt, true); + + for (int ii = 0; ii < script->scripts.count(); ++ii) + ctxt->importedScripts << run(ctxt, script->scripts.at(ii)->scriptData()); + } + + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + if (shared) { + scriptContext->pushScope(enginePriv->contextClass->newUrlContext(script->url.toString())); // XXX toString()? + } else { + scriptContext->pushScope(enginePriv->contextClass->newUrlContext(ctxt, 0, script->url.toString())); + } + + scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject()); + QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine); + scriptContext->pushScope(scope); + + scriptEngine->evaluate(script->m_program); + + if (scriptEngine->hasUncaughtException()) { + QDeclarativeError error; + QDeclarativeExpressionPrivate::exceptionToError(scriptEngine, error); + enginePriv->warning(error); + } + + scriptEngine->popContext(); + + if (shared) { + script->m_loaded = true; + script->m_value = scope; + } + + return scope; +} QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativevme_p.h b/src/declarative/qml/qdeclarativevme_p.h index 77c016c1a8..4c010f19d8 100644 --- a/src/declarative/qml/qdeclarativevme_p.h +++ b/src/declarative/qml/qdeclarativevme_p.h @@ -62,6 +62,8 @@ QT_BEGIN_NAMESPACE class QObject; +class QScriptValue; +class QDeclarativeScriptData; class QDeclarativeInstruction; class QDeclarativeCompiledData; class QDeclarativeCompiledData; @@ -103,6 +105,8 @@ public: QObject *run(QDeclarativeContextData *, QDeclarativeCompiledData *, int start = -1, int count = -1, const QBitField & = QBitField()); + QScriptValue run(QDeclarativeContextData *, QDeclarativeScriptData *); + void runDeferred(QObject *); bool isError() const; diff --git a/src/declarative/qml/qdeclarativevmemetaobject.cpp b/src/declarative/qml/qdeclarativevmemetaobject.cpp index ad1bf0dd3a..6eb74b3bad 100644 --- a/src/declarative/qml/qdeclarativevmemetaobject.cpp +++ b/src/declarative/qml/qdeclarativevmemetaobject.cpp @@ -647,6 +647,7 @@ int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) return -1; // We can't run the method QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); + ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. QScriptValue function = method(id); @@ -657,10 +658,19 @@ int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) args << ep->scriptValueFromVariant(*(QVariant *)a[ii + 1]); } } + QScriptValue rv = function.call(ep->objectClass->newQObject(object), args); + if (ep->scriptEngine.hasUncaughtException()) { + QDeclarativeError error; + QDeclarativeExpressionPrivate::exceptionToError(&ep->scriptEngine, error); + if (error.isValid()) { + ep->warning(error); + } + } if (a[0]) *reinterpret_cast<QVariant *>(a[0]) = ep->scriptValueToVariant(rv); + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. return -1; } return -1; diff --git a/src/declarative/qml/qdeclarativexmlhttprequest.cpp b/src/declarative/qml/qdeclarativexmlhttprequest.cpp index aefc896f5b..930d345285 100644 --- a/src/declarative/qml/qdeclarativexmlhttprequest.cpp +++ b/src/declarative/qml/qdeclarativexmlhttprequest.cpp @@ -101,6 +101,8 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(xhrDump, QML_XHR_DUMP); +namespace { + class DocumentImpl; class NodeImpl { @@ -323,6 +325,8 @@ public: static QScriptValue load(QScriptEngine *engine, const QByteArray &data); }; +} + QT_END_NAMESPACE Q_DECLARE_METATYPE(Node) @@ -1231,6 +1235,18 @@ void QDeclarativeXMLHttpRequest::downloadProgress(qint64 bytes) } } +static const char *errorToString(QNetworkReply::NetworkError error) +{ + int idx = QNetworkReply::staticMetaObject.indexOfEnumerator("NetworkError"); + if (idx == -1) return "EnumLookupFailed"; + + QMetaEnum e = QNetworkReply::staticMetaObject.enumerator(idx); + + const char *name = e.valueToKey(error); + if (!name) return "EnumLookupFailed"; + else return name; +} + void QDeclarativeXMLHttpRequest::error(QNetworkReply::NetworkError error) { Q_UNUSED(error) @@ -1245,6 +1261,11 @@ void QDeclarativeXMLHttpRequest::error(QNetworkReply::NetworkError error) m_data.clear(); destroyNetwork(); + if (xhrDump()) { + qWarning().nospace() << "XMLHttpRequest: ERROR " << qPrintable(m_url.toString()); + qWarning().nospace() << " " << error << " " << errorToString(error) << " " << m_statusText; + } + if (error == QNetworkReply::ContentAccessDenied || error == QNetworkReply::ContentOperationNotPermittedError || error == QNetworkReply::ContentNotFoundError || diff --git a/src/declarative/qml/qintrusivelist.cpp b/src/declarative/qml/qintrusivelist.cpp new file mode 100644 index 0000000000..6c27af0777 --- /dev/null +++ b/src/declarative/qml/qintrusivelist.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qintrusivelist_p.h" + +/*! +\class QIntrusiveList +\brief The QIntrusiveList class is a template class that provides a list of objects using static storage. +\internal + +QIntrusiveList creates a linked list of objects. Adding and removing objects from the +QIntrusiveList is a constant time operation and is very quick. The list performs no memory +allocations, but does require the objects being added to the list to contain a QIntrusiveListNode +instance for the list's use. Even so, for small lists QIntrusiveList uses less memory than Qt's +other list classes. + +As QIntrusiveList uses storage inside the objects in the list, each object can only be in one +list at a time. Objects are inserted by the insert() method. If the object is already +in a list (including the one it is being inserted into) it is first removed, and then inserted +at the head of the list. QIntrusiveList is a last-in-first-out list. That is, following an +insert() the inserted object becomes the list's first() object. + +\code +struct MyObject { + MyObject(int value) : value(value) {} + + int value; + QIntrusiveListNode node; +}; +typedef QIntrusiveList<MyObject, &MyObject::node> MyObjectList; + +void foo() { + MyObjectList list; + + MyObject m0(0); + MyObject m1(1); + MyObject m2(2); + + list.insert(&m0); + list.insert(&m1); + list.insert(&m2); + + // QIntrusiveList is LIFO, so will print: 2... 1... 0... + for (MyObjectList::iterator iter = list.begin(); iter != list.end(); ++iter) { + qWarning() << iter->value; + } +} +\endcode +*/ + + +/*! +\fn QIntrusiveList::QIntrusiveList(); + +Construct an empty list. +*/ + +/*! +\fn QIntrusiveList::~QIntrusiveList(); + +Destroy the list. All entries are removed. +*/ + +/*! +\fn void QIntrusiveList::insert(N *object); + +Insert \a object into the list. If \a object is a member of this, or another list, it will be +removed and inserted at the head of this list. +*/ + +/*! +\fn void QIntrusiveList::remove(N *object); + +Remove \a object from the list. \a object must not be null. +*/ + +/*! +\fn N *QIntrusiveList::first() const + +Returns the first entry in this list, or null if the list is empty. +*/ + +/*! +\fn N *QIntrusiveList::next(N *current) + +Returns the next object after \a current, or null if \a current is the last object. \a current cannot be null. +*/ + +/*! +\fn iterator QIntrusiveList::begin() + +Returns an STL-style interator pointing to the first item in the list. + +\sa end() +*/ + +/*! +\fn iterator QIntrusiveList::end() + +Returns an STL-style iterator pointing to the imaginary item after the last item in the list. + +\sa begin() +*/ + +/*! +iterator &QInplacelist::iterator::erase() + +Remove the current object from the list, and return an iterator to the next element. +*/ + + +/*! +\fn QIntrusiveListNode::QIntrusiveListNode() + +Create a QIntrusiveListNode. +*/ + +/*! +\fn QIntrusiveListNode::~QIntrusiveListNode() + +Destroy the QIntrusiveListNode. If the node is in a list, it is removed. +*/ + +/*! +\fn void QIntrusiveListNode::remove() + +If in a list, remove this node otherwise do nothing. +*/ + +/*! +\fn bool QIntrusiveListNode::isInList() const + +Returns true if this node is in a list, false otherwise. +*/ + diff --git a/src/declarative/qml/qintrusivelist_p.h b/src/declarative/qml/qintrusivelist_p.h new file mode 100644 index 0000000000..459d051d07 --- /dev/null +++ b/src/declarative/qml/qintrusivelist_p.h @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QINTRUSIVELIST_P_H +#define QINTRUSIVELIST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QIntrusiveListNode; +template<class N, QIntrusiveListNode N::*member> +class QIntrusiveList +{ +public: + inline QIntrusiveList(); + inline ~QIntrusiveList(); + + inline void insert(N *n); + inline void remove(N *n); + + class iterator { + public: + inline iterator(); + inline iterator(N *value); + + inline N *operator*() const; + inline N *operator->() const; + inline bool operator==(const iterator &other) const; + inline bool operator!=(const iterator &other) const; + inline iterator &operator++(); + + inline iterator &erase(); + + private: + N *_value; + }; + typedef iterator Iterator; + + inline N *first() const; + static inline N *next(N *current); + + inline iterator begin(); + inline iterator end(); + +private: + static inline N *nodeToN(QIntrusiveListNode *node); + + QIntrusiveListNode *__first; +}; + +class QIntrusiveListNode +{ +public: + inline QIntrusiveListNode(); + inline ~QIntrusiveListNode(); + + inline void remove(); + inline bool isInList() const; + + QIntrusiveListNode *_next; + QIntrusiveListNode**_prev; +}; + +template<class N, QIntrusiveListNode N::*member> +QIntrusiveList<N, member>::iterator::iterator() +: _value(0) +{ +} + +template<class N, QIntrusiveListNode N::*member> +QIntrusiveList<N, member>::iterator::iterator(N *value) +: _value(value) +{ +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::iterator::operator*() const +{ + return _value; +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::iterator::operator->() const +{ + return _value; +} + +template<class N, QIntrusiveListNode N::*member> +bool QIntrusiveList<N, member>::iterator::operator==(const iterator &other) const +{ + return other._value == _value; +} + +template<class N, QIntrusiveListNode N::*member> +bool QIntrusiveList<N, member>::iterator::operator!=(const iterator &other) const +{ + return other._value != _value; +} + +template<class N, QIntrusiveListNode N::*member> +typename QIntrusiveList<N, member>::iterator &QIntrusiveList<N, member>::iterator::operator++() +{ + _value = QIntrusiveList<N, member>::next(_value); + return *this; +} + +template<class N, QIntrusiveListNode N::*member> +typename QIntrusiveList<N, member>::iterator &QIntrusiveList<N, member>::iterator::erase() +{ + N *old = _value; + _value = QIntrusiveList<N, member>::next(_value); + (old->*member).remove(); + return *this; +} + +template<class N, QIntrusiveListNode N::*member> +QIntrusiveList<N, member>::QIntrusiveList() +: __first(0) +{ +} + +template<class N, QIntrusiveListNode N::*member> +QIntrusiveList<N, member>::~QIntrusiveList() +{ + while (__first) __first->remove(); +} + +template<class N, QIntrusiveListNode N::*member> +void QIntrusiveList<N, member>::insert(N *n) +{ + QIntrusiveListNode *nnode = &(n->*member); + nnode->remove(); + + nnode->_next = __first; + if (nnode->_next) nnode->_next->_prev = &nnode->_next; + __first = nnode; + nnode->_prev = &__first; +} + +template<class N, QIntrusiveListNode N::*member> +void QIntrusiveList<N, member>::remove(N *n) +{ + QIntrusiveListNode *nnode = &(n->*member); + nnode->remove(); +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::first() const +{ + return __first?nodeToN(__first):0; +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::next(N *current) +{ + QIntrusiveListNode *nextnode = (current->*member)._next; + N *nextstruct = nextnode?nodeToN(nextnode):0; + return nextstruct; +} + +template<class N, QIntrusiveListNode N::*member> +typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::begin() +{ + return __first?iterator(nodeToN(__first)):iterator(); +} + +template<class N, QIntrusiveListNode N::*member> +typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::end() +{ + return iterator(); +} + +template<class N, QIntrusiveListNode N::*member> +N *QIntrusiveList<N, member>::nodeToN(QIntrusiveListNode *node) +{ + return (N *)((char *)node - ((char *)&(((N *)0)->*member) - (char *)0)); +} + +QIntrusiveListNode::QIntrusiveListNode() +: _next(0), _prev(0) +{ +} + +QIntrusiveListNode::~QIntrusiveListNode() +{ + remove(); +} + +void QIntrusiveListNode::remove() +{ + if (_prev) *_prev = _next; + if (_next) _next->_prev = _prev; + _prev = 0; + _next = 0; +} + +bool QIntrusiveListNode::isInList() const +{ + return _prev != 0; +} + +QT_END_NAMESPACE + +#endif // QINTRUSIVELIST_P_H diff --git a/src/declarative/qml/qmetaobjectbuilder.cpp b/src/declarative/qml/qmetaobjectbuilder.cpp index dc941e2601..a63656bc28 100644 --- a/src/declarative/qml/qmetaobjectbuilder.cpp +++ b/src/declarative/qml/qmetaobjectbuilder.cpp @@ -101,7 +101,7 @@ bool isVariantType(const char* type) return qvariant_nameToType(type) != 0; } -// copied from qmetaobject.cpp +// copied from qmetaobject_p.h // do not touch without touching the moc as well enum PropertyFlags { Invalid = 0x00000000, @@ -111,6 +111,8 @@ enum PropertyFlags { EnumOrFlag = 0x00000008, StdCppSet = 0x00000100, // Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, Designable = 0x00001000, ResolveDesignable = 0x00002000, Scriptable = 0x00004000, @@ -618,6 +620,8 @@ QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty& protot property.setUser(prototype.isUser()); property.setStdCppSet(prototype.hasStdCppSet()); property.setEnumOrFlag(prototype.isEnumType()); + property.setConstant(prototype.isConstant()); + property.setFinal(prototype.isFinal()); if (prototype.hasNotifySignal()) { // Find an existing method for the notify signal, or add a new one. QMetaMethod method = prototype.notifySignal(); @@ -2278,6 +2282,32 @@ bool QMetaPropertyBuilder::isEnumOrFlag() const } /*! + Returns true if the property is constant; otherwise returns false. + The default value is false. +*/ +bool QMetaPropertyBuilder::isConstant() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Constant); + else + return false; +} + +/*! + Returns true if the property is final; otherwise returns false. + The default value is false. +*/ +bool QMetaPropertyBuilder::isFinal() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Final); + else + return false; +} + +/*! Sets this property to readable if \a value is true. \sa isReadable(), setWritable() @@ -2401,6 +2431,31 @@ void QMetaPropertyBuilder::setEnumOrFlag(bool value) } /*! + Sets the \c CONSTANT flag on this property to \a value. + + \sa isConstant() +*/ +void QMetaPropertyBuilder::setConstant(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Constant, value); +} + +/*! + Sets the \c FINAL flag on this property to \a value. + + \sa isFinal() +*/ +void QMetaPropertyBuilder::setFinal(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Final, value); +} + + +/*! \class QMetaEnumBuilder \internal \brief The QMetaEnumBuilder class enables modifications to an enumerator definition on a meta object builder. diff --git a/src/declarative/qml/qmetaobjectbuilder_p.h b/src/declarative/qml/qmetaobjectbuilder_p.h index d7085f8784..335a825be3 100644 --- a/src/declarative/qml/qmetaobjectbuilder_p.h +++ b/src/declarative/qml/qmetaobjectbuilder_p.h @@ -258,6 +258,8 @@ public: bool isUser() const; bool hasStdCppSet() const; bool isEnumOrFlag() const; + bool isConstant() const; + bool isFinal() const; void setReadable(bool value); void setWritable(bool value); @@ -269,6 +271,8 @@ public: void setUser(bool value); void setStdCppSet(bool value); void setEnumOrFlag(bool value); + void setConstant(bool value); + void setFinal(bool value); private: const QMetaObjectBuilder *_mobj; diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index bf9e54a986..62c1f97d60 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -18,7 +18,6 @@ SOURCES += \ $$PWD/qdeclarativecompiler.cpp \ $$PWD/qdeclarativecompileddata.cpp \ $$PWD/qdeclarativeboundsignal.cpp \ - $$PWD/qdeclarativedom.cpp \ $$PWD/qdeclarativerefcount.cpp \ $$PWD/qdeclarativemetatype.cpp \ $$PWD/qdeclarativestringconverters.cpp \ @@ -30,7 +29,6 @@ SOURCES += \ $$PWD/qdeclarativeenginedebug.cpp \ $$PWD/qdeclarativerewrite.cpp \ $$PWD/qdeclarativevaluetype.cpp \ - $$PWD/qdeclarativecompiledbindings.cpp \ $$PWD/qdeclarativefastproperties.cpp \ $$PWD/qdeclarativexmlhttprequest.cpp \ $$PWD/qdeclarativesqldatabase.cpp \ @@ -44,6 +42,7 @@ SOURCES += \ $$PWD/qdeclarativetypenamecache.cpp \ $$PWD/qdeclarativescriptstring.cpp \ $$PWD/qdeclarativeobjectscriptclass.cpp \ + $$PWD/qdeclarativescarceresourcescriptclass.cpp \ $$PWD/qdeclarativecontextscriptclass.cpp \ $$PWD/qdeclarativeglobalscriptclass.cpp \ $$PWD/qdeclarativevaluetypescriptclass.cpp \ @@ -56,7 +55,8 @@ SOURCES += \ $$PWD/qdeclarativeextensionplugin.cpp \ $$PWD/qdeclarativeimport.cpp \ $$PWD/qdeclarativelist.cpp \ - $$PWD/qperformancetimer.cpp + $$PWD/qperformancetimer.cpp \ + $$PWD/qintrusivelist.cpp \ HEADERS += \ $$PWD/qdeclarativeparser_p.h \ @@ -81,8 +81,6 @@ HEADERS += \ $$PWD/qdeclarativeengine_p.h \ $$PWD/qdeclarativeexpression_p.h \ $$PWD/qdeclarativeprivate.h \ - $$PWD/qdeclarativedom_p.h \ - $$PWD/qdeclarativedom_p_p.h \ $$PWD/qdeclarativerefcount_p.h \ $$PWD/qdeclarativemetatype_p.h \ $$PWD/qdeclarativeengine.h \ @@ -104,7 +102,6 @@ HEADERS += \ $$PWD/qpodvector_p.h \ $$PWD/qbitfield_p.h \ $$PWD/qdeclarativevaluetype_p.h \ - $$PWD/qdeclarativecompiledbindings_p.h \ $$PWD/qdeclarativefastproperties_p.h \ $$PWD/qdeclarativexmlhttprequest_p.h \ $$PWD/qdeclarativesqldatabase_p.h \ @@ -118,6 +115,7 @@ HEADERS += \ $$PWD/qdeclarativetypenamecache_p.h \ $$PWD/qdeclarativescriptstring.h \ $$PWD/qdeclarativeobjectscriptclass_p.h \ + $$PWD/qdeclarativescarceresourcescriptclass_p.h \ $$PWD/qdeclarativecontextscriptclass_p.h \ $$PWD/qdeclarativeglobalscriptclass_p.h \ $$PWD/qdeclarativevaluetypescriptclass_p.h \ @@ -131,11 +129,13 @@ HEADERS += \ $$PWD/qdeclarativeextensioninterface.h \ $$PWD/qdeclarativeimport_p.h \ $$PWD/qdeclarativeextensionplugin.h \ - $$PWD/qperformancetimer_p.h + $$PWD/qperformancetimer_p.h \ + $$PWD/qintrusivelist_p.h \ QT += sql include(parser/parser.pri) include(rewriter/rewriter.pri) +include(v4/v4.pri) # mirrors logic in corelib/kernel/kernel.pri unix:!symbian: contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) diff --git a/src/declarative/qml/v4/qdeclarativev4bindings.cpp b/src/declarative/qml/v4/qdeclarativev4bindings.cpp new file mode 100644 index 0000000000..80c7a68697 --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4bindings.cpp @@ -0,0 +1,1530 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define REGISTER_CLEANUP_DEBUG + +#include "private/qdeclarativev4bindings_p.h" +#include "private/qdeclarativev4program_p.h" +#include "private/qdeclarativev4compiler_p.h" + +#include <private/qdeclarativefastproperties_p.h> +#include <private/qdeclarativedebugtrace_p.h> +#include <private/qdeclarativeanchors_p_p.h> // For AnchorLine +#include <private/qsganchors_p_p.h> // For AnchorLine + +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <math.h> // ::fmod + +QT_BEGIN_NAMESPACE + +using namespace QDeclarativeJS; + +namespace { +struct Register { + typedef QDeclarativeRegisterType Type; + + void setUndefined() { dataType = UndefinedType; } + void setNaN() { setqreal(qSNaN()); } + bool isUndefined() const { return dataType == UndefinedType; } + + void setQObject(QObject *o) { *((QObject **)data) = o; dataType = QObjectStarType; } + QObject *getQObject() const { return *((QObject **)data); } + + void setqreal(qreal v) { *((qreal *)data) = v; dataType = QRealType; } + qreal getqreal() const { return *((qreal *)data); } + qreal &getqrealref() const { return *((qreal *)data); } + + void setint(int v) { *((int *)data) = v; dataType = IntType; } + int getint() const { return *((int *)data); } + int &getintref() const { return *((int *)data); } + + void setbool(bool v) { *((bool *)data) = v; dataType = BoolType; } + bool getbool() const { return *((bool *)data); } + bool &getboolref() 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; } + + Type gettype() const { return dataType; } + void settype(Type t) { dataType = t; } + + // int type; // Optional type + + Type dataType; // Type of data + void *data[2]; // Object stored here + + inline void cleanup(); + inline void cleanupString(); + inline void cleanupUrl(); + inline void cleanupVariant(); + + inline void copy(const Register &other); + inline void init(Type type); +#ifdef REGISTER_CLEANUP_DEBUG + Register() { + type = 0; + } + + ~Register() { + if (dataType >= FirstCleanupType) + qWarning("Register leaked of type %d", dataType); + } +#endif +}; + +void Register::cleanup() +{ + if (dataType >= FirstCleanupType) { + if (dataType == QStringType) { + getstringptr()->~QString(); + } else if (dataType == QUrlType) { + geturlptr()->~QUrl(); + } else if (dataType == QVariantType) { + getvariantptr()->~QVariant(); + } + } + setUndefined(); +} + +void Register::cleanupString() +{ + getstringptr()->~QString(); + setUndefined(); +} + +void Register::cleanupUrl() +{ + geturlptr()->~QUrl(); + setUndefined(); +} + +void Register::cleanupVariant() +{ + getvariantptr()->~QVariant(); + setUndefined(); +} + +void Register::copy(const Register &other) +{ + *this = other; + if (other.dataType >= FirstCleanupType) { + if (other.dataType == QStringType) + new (getstringptr()) QString(*other.getstringptr()); + else if (other.dataType == QUrlType) + new (geturlptr()) QUrl(*other.geturlptr()); + else if (other.dataType == QVariantType) + new (getvariantptr()) QVariant(*other.getvariantptr()); + } +} + +void Register::init(Type type) +{ + dataType = type; + if (dataType >= FirstCleanupType) { + if (dataType == QStringType) + new (getstringptr()) QString(); + else if (dataType == QUrlType) + new (geturlptr()) QUrl(); + else if (dataType == QVariantType) + new (getvariantptr()) QVariant(); + } +} + +} // end of anonymous namespace + +class QDeclarativeV4BindingsPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeV4Bindings) + +public: + QDeclarativeV4BindingsPrivate(); + virtual ~QDeclarativeV4BindingsPrivate(); + + struct Binding : public QDeclarativeAbstractBinding, public QDeclarativeDelayedError { + Binding() : enabled(false), updating(0), property(0), + scope(0), target(0), executedBlocks(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; + quint32 executedBlocks; + + QDeclarativeV4BindingsPrivate *parent; + }; + + typedef QDeclarativeNotifierEndpoint Subscription; + Subscription *subscriptions; + QScriptDeclarativeClass::PersistentIdentifier *identifiers; + + void run(Binding *, QDeclarativePropertyPrivate::WriteFlags flags); + + QDeclarativeV4Program *program; + Binding *bindings; + + static int methodCount; + + void init(); + void run(int instr, quint32 &executedBlocks, QDeclarativeContextData *context, + QDeclarativeDelayedError *error, QObject *scope, QObject *output, + QDeclarativePropertyPrivate::WriteFlags storeFlags +#ifdef QML_THREADED_INTERPRETER + , void ***decode_instr = 0 +#endif + ); + + + inline void unsubscribe(int subIndex); + inline void subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex); + inline void subscribe(QObject *o, int notifyIndex, int subIndex); + + inline static qint32 toInt32(qsreal n); + static const qsreal D32; + static quint32 toUint32(qsreal n); +}; + +QDeclarativeV4BindingsPrivate::QDeclarativeV4BindingsPrivate() +: subscriptions(0), identifiers(0), program(0), bindings(0) +{ +} + +QDeclarativeV4BindingsPrivate::~QDeclarativeV4BindingsPrivate() +{ + delete [] subscriptions; subscriptions = 0; + delete [] identifiers; identifiers = 0; +} + +int QDeclarativeV4BindingsPrivate::methodCount = -1; + +QDeclarativeV4Bindings::QDeclarativeV4Bindings(const char *program, QDeclarativeContextData *context) +: QObject(*(new QDeclarativeV4BindingsPrivate)) +{ + Q_D(QDeclarativeV4Bindings); + + if (d->methodCount == -1) + d->methodCount = QDeclarativeV4Bindings::staticMetaObject.methodCount(); + + d->program = (QDeclarativeV4Program *)program; + + if (program) { + d->init(); + + QDeclarativeAbstractExpression::setContext(context); + } +} + +QDeclarativeV4Bindings::~QDeclarativeV4Bindings() +{ + Q_D(QDeclarativeV4Bindings); + + delete [] d->bindings; +} + +QDeclarativeAbstractBinding *QDeclarativeV4Bindings::configBinding(int index, QObject *target, + QObject *scope, int property) +{ + Q_D(QDeclarativeV4Bindings); + + QDeclarativeV4BindingsPrivate::Binding *rv = d->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 QDeclarativeV4BindingsPrivate::Binding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags) +{ + if (enabled != e) { + enabled = e; + + if (e) update(flags); + } +} + +void QDeclarativeV4BindingsPrivate::Binding::update(QDeclarativePropertyPrivate::WriteFlags flags) +{ + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Binding); + parent->run(this, flags); + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Binding); +} + +void QDeclarativeV4BindingsPrivate::Binding::destroy() +{ + enabled = false; + removeFromObject(); + clear(); + parent->q_func()->release(); +} + +int QDeclarativeV4Bindings::qt_metacall(QMetaObject::Call c, int id, void **) +{ + Q_D(QDeclarativeV4Bindings); + + if (c == QMetaObject::InvokeMetaMethod && id >= d->methodCount) { + id -= d->methodCount; + + QDeclarativeV4Program::BindingReferenceList *list = d->program->signalTable(id); + + for (quint32 ii = 0; ii < list->count; ++ii) { + QDeclarativeV4Program::BindingReference *bindingRef = list->bindings + ii; + + QDeclarativeV4BindingsPrivate::Binding *binding = d->bindings + bindingRef->binding; + if (binding->executedBlocks & bindingRef->blockMask) + d->run(binding, QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + return -1; +} + +void QDeclarativeV4BindingsPrivate::run(Binding *binding, QDeclarativePropertyPrivate::WriteFlags flags) +{ + Q_Q(QDeclarativeV4Bindings); + + 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("QDeclarativeV4Bindings", "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, binding->executedBlocks, context, binding, binding->scope, target, flags); + + vt->write(binding->target, binding->property & 0xFFFF, flags); + } else { + run(binding->index, binding->executedBlocks, context, binding, binding->scope, binding->target, flags); + } + binding->updating = false; +} + + +void QDeclarativeV4BindingsPrivate::unsubscribe(int subIndex) +{ + QDeclarativeV4BindingsPrivate::Subscription *sub = (subscriptions + subIndex); + sub->disconnect(); +} + +void QDeclarativeV4BindingsPrivate::subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex) +{ + Q_Q(QDeclarativeV4Bindings); + + unsubscribe(subIndex); + + if (p->idValues[idIndex]) { + QDeclarativeV4BindingsPrivate::Subscription *sub = (subscriptions + subIndex); + sub->target = q; + sub->targetMethod = methodCount + subIndex; + sub->connect(&p->idValues[idIndex].bindings); + } +} + +void QDeclarativeV4BindingsPrivate::subscribe(QObject *o, int notifyIndex, int subIndex) +{ + Q_Q(QDeclarativeV4Bindings); + + QDeclarativeV4BindingsPrivate::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; + } +} + +void QDeclarativeV4BindingsPrivate::init() +{ + if (program->subscriptions) + subscriptions = new QDeclarativeV4BindingsPrivate::Subscription[program->subscriptions]; + if (program->identifiers) + identifiers = new QScriptDeclarativeClass::PersistentIdentifier[program->identifiers]; + + bindings = new QDeclarativeV4BindingsPrivate::Binding[program->bindings]; +} + +static bool testCompareVariants(const QVariant &qtscriptRaw, const QVariant &v4) +{ + QVariant qtscript = qtscriptRaw; + + if (qtscript.userType() == v4.userType()) { + } else if (qtscript.canConvert((QVariant::Type)v4.userType())) { + qtscript.convert((QVariant::Type)v4.userType()); + } else if (qtscript.userType() == QVariant::Invalid && v4.userType() == QMetaType::QObjectStar) { + qtscript = qVariantFromValue<QObject *>(0); + } else { + return false; + } + + int type = qtscript.userType(); + + if (type == qMetaTypeId<QDeclarativeAnchorLine>()) { + QDeclarativeAnchorLine la = qvariant_cast<QDeclarativeAnchorLine>(qtscript); + QDeclarativeAnchorLine ra = qvariant_cast<QDeclarativeAnchorLine>(v4); + + return la == ra; + } else if (type == qMetaTypeId<QSGAnchorLine>()) { + QSGAnchorLine la = qvariant_cast<QSGAnchorLine>(qtscript); + QSGAnchorLine ra = qvariant_cast<QSGAnchorLine>(v4); + + return la == ra; + } else if (type == QMetaType::Double) { + + double la = qvariant_cast<double>(qtscript); + double lr = qvariant_cast<double>(v4); + + return la == lr || (qIsNaN(la) && qIsNaN(lr)); + + } else if (type == QMetaType::Float) { + + float la = qvariant_cast<float>(qtscript); + float lr = qvariant_cast<float>(v4); + + return la == lr || (qIsNaN(la) && qIsNaN(lr)); + + } else { + return qtscript == v4; + } +} + +QByteArray testResultToString(const QVariant &result, bool undefined) +{ + if (undefined) { + return "undefined"; + } else { + QString rv; + QDebug d(&rv); + d << result; + return rv.toUtf8(); + } +} + +static void testBindingResult(const QString &binding, int line, int column, + QDeclarativeContextData *context, QObject *scope, + const Register &result, int resultType) +{ + QDeclarativeExpression expression(context->asQDeclarativeContext(), scope, binding); + bool isUndefined = false; + QVariant value = expression.evaluate(&isUndefined); + + bool iserror = false; + QByteArray qtscriptResult; + QByteArray v4Result; + + if (expression.hasError()) { + iserror = true; + qtscriptResult = "exception"; + } else { + qtscriptResult = testResultToString(value, isUndefined); + } + + if (isUndefined && result.isUndefined()) { + return; + } else if(isUndefined != result.isUndefined()) { + iserror = true; + } + + QVariant v4value; + if (!result.isUndefined()) { + switch (resultType) { + case QMetaType::QString: + v4value = *result.getstringptr(); + break; + case QMetaType::QUrl: + v4value = *result.geturlptr(); + break; + case QMetaType::QObjectStar: + v4value = qVariantFromValue<QObject *>(result.getQObject()); + break; + case QMetaType::Bool: + v4value = result.getbool(); + break; + case QMetaType::Int: + v4value = result.getint(); + break; + case QMetaType::QReal: + v4value = result.getqreal(); + break; + default: + if (resultType == qMetaTypeId<QDeclarativeAnchorLine>()) { + v4value = qVariantFromValue<QDeclarativeAnchorLine>(*(QDeclarativeAnchorLine *)result.typeDataPtr()); + } else if (resultType == qMetaTypeId<QSGAnchorLine>()) { + v4value = qVariantFromValue<QSGAnchorLine>(*(QSGAnchorLine *)result.typeDataPtr()); + } else { + iserror = true; + v4Result = "Unknown V4 type"; + } + } + } + if (v4Result.isEmpty()) + v4Result = testResultToString(v4value, result.isUndefined()); + + if (!testCompareVariants(value, v4value)) + iserror = true; + + if (iserror) { + qWarning().nospace() << "QDeclarativeV4: Optimization error @" << context->url.toString().toUtf8().constData() << ":" << line << ":" << column; + + qWarning().nospace() << " Binding: " << binding; + qWarning().nospace() << " QtScript: " << qtscriptResult.constData(); + qWarning().nospace() << " V4: " << v4Result.constData(); + } +} + +static void testBindingException(const QString &binding, int line, int column, + QDeclarativeContextData *context, QObject *scope) +{ + QDeclarativeExpression expression(context->asQDeclarativeContext(), scope, binding); + bool isUndefined = false; + QVariant value = expression.evaluate(&isUndefined); + + if (!expression.hasError()) { + QByteArray qtscriptResult = testResultToString(value, isUndefined); + qWarning().nospace() << "QDeclarativeV4: Optimization error @" << context->url.toString().toUtf8().constData() << ":" << line << ":" << column; + qWarning().nospace() << " Binding: " << binding; + qWarning().nospace() << " QtScript: " << qtscriptResult.constData(); + qWarning().nospace() << " V4: exception"; + } +} + +static void throwException(int id, QDeclarativeDelayedError *error, + QDeclarativeV4Program *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); +} + +const qsreal QDeclarativeV4BindingsPrivate::D32 = 4294967296.0; + +qint32 QDeclarativeV4BindingsPrivate::toInt32(qsreal n) +{ + if (qIsNaN(n) || qIsInf(n) || (n == 0)) + return 0; + + double sign = (n < 0) ? -1.0 : 1.0; + qsreal abs_n = fabs(n); + + n = ::fmod(sign * ::floor(abs_n), D32); + const double D31 = D32 / 2.0; + + if (sign == -1 && n < -D31) + n += D32; + + else if (sign != -1 && n >= D31) + n -= D32; + + return qint32 (n); +} + +inline quint32 QDeclarativeV4BindingsPrivate::toUint32(qsreal n) +{ + if (qIsNaN(n) || qIsInf(n) || (n == 0)) + return 0; + + double sign = (n < 0) ? -1.0 : 1.0; + qsreal abs_n = fabs(n); + + n = ::fmod(sign * ::floor(abs_n), D32); + + if (n < 0) + n += D32; + + return quint32 (n); +} + +#define THROW_EXCEPTION_STR(id, str) { \ + if (testBinding) testBindingException(*testBindingSource, bindingLine, bindingColumn, context, scope); \ + throwException((id), error, program, context, (str)); \ + goto exceptionExit; \ +} + +#define THROW_EXCEPTION(id) THROW_EXCEPTION_STR(id, QString()) + +#define MARK_REGISTER(reg) cleanupRegisterMask |= (1 << (reg)) +#define MARK_CLEAN_REGISTER(reg) cleanupRegisterMask &= ~(1 << (reg)) + +#define STRING_REGISTER(reg) { \ + registers[(reg)].settype(QStringType); \ + MARK_REGISTER(reg); \ +} + +#define URL_REGISTER(reg) { \ + registers[(reg)].settype(QUrlType); \ + MARK_REGISTER(reg); \ +} + +#define VARIANT_REGISTER(reg) { \ + registers[(reg)].settype(QVariantType); \ + MARK_REGISTER(reg); \ +} + +#ifdef QML_THREADED_INTERPRETER +void **QDeclarativeV4Bindings::getDecodeInstrTable() +{ + static void **decode_instr; + if (!decode_instr) { + QDeclarativeV4Bindings dummy(0, 0); + quint32 executedBlocks = 0; + dummy.d_func()->run(0, executedBlocks, 0, 0, 0, 0, QDeclarativePropertyPrivate::BypassInterceptor, &decode_instr); + } + return decode_instr; +} +#endif + +void QDeclarativeV4BindingsPrivate::run(int instrIndex, quint32 &executedBlocks, + QDeclarativeContextData *context, QDeclarativeDelayedError *error, + QObject *scope, QObject *output, QDeclarativePropertyPrivate::WriteFlags storeFlags +#ifdef QML_THREADED_INTERPRETER + ,void ***table +#endif + ) +{ + Q_Q(QDeclarativeV4Bindings); + +#ifdef QML_THREADED_INTERPRETER + if (table) { + static void *decode_instr[] = { + FOR_EACH_QML_INSTR(QML_INSTR_ADDR) + }; + + *table = decode_instr; + return; + } +#endif + + + error->removeError(); + + Register registers[32]; + quint32 cleanupRegisterMask = 0; + + executedBlocks = 0; + + QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(context->engine); + const char *code = program->instructions(); + code += instrIndex * QML_INSTR_SIZE(Jump, jump); + const Instr *instr = (const Instr *) code; + + const char *data = program->data(); + + QString *testBindingSource = 0; + bool testBinding = false; + int bindingLine = 0; + int bindingColumn = 0; + +#ifdef QML_THREADED_INTERPRETER + goto *instr->common.code; +#else + for (;;) { + switch (instr->common.type) { +#endif + + QML_BEGIN_INSTR(Noop, common) + QML_END_INSTR(Noop, common) + + QML_BEGIN_INSTR(BindingId, id) + bindingLine = instr->id.line; + bindingColumn = instr->id.column; + QML_END_INSTR(BindingId, id) + + QML_BEGIN_INSTR(SubscribeId, subscribeop) + subscribeId(context, instr->subscribeop.index, instr->subscribeop.offset); + QML_END_INSTR(SubscribeId, subscribeop) + + QML_BEGIN_INSTR(Subscribe, subscribeop) + { + QObject *o = 0; + const Register &object = registers[instr->subscribeop.reg]; + if (!object.isUndefined()) o = object.getQObject(); + subscribe(o, instr->subscribeop.index, instr->subscribeop.offset); + } + QML_END_INSTR(Subscribe, subscribeop) + + QML_BEGIN_INSTR(FetchAndSubscribe, fetchAndSubscribe) + { + Register ® = registers[instr->fetchAndSubscribe.reg]; + + if (reg.isUndefined()) + THROW_EXCEPTION(instr->fetchAndSubscribe.exceptionId); + + QObject *object = reg.getQObject(); + if (!object) { + reg.setUndefined(); + } else { + int subIdx = instr->fetchAndSubscribe.subscription; + QDeclarativeV4BindingsPrivate::Subscription *sub = 0; + if (subIdx != -1) { + sub = (subscriptions + subIdx); + sub->target = q; + sub->targetMethod = methodCount + subIdx; + } + reg.init((Register::Type)instr->fetchAndSubscribe.valueType); + if (instr->fetchAndSubscribe.valueType >= FirstCleanupType) + MARK_REGISTER(instr->fetchAndSubscribe.reg); + QDeclarativeV4Compiler::fastPropertyAccessor()->accessor(instr->fetchAndSubscribe.function)(object, reg.typeDataPtr(), sub); + } + } + QML_END_INSTR(FetchAndSubscribe, fetchAndSubscribe) + + QML_BEGIN_INSTR(LoadId, load) + registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data()); + QML_END_INSTR(LoadId, load) + + QML_BEGIN_INSTR(LoadScope, load) + registers[instr->load.reg].setQObject(scope); + QML_END_INSTR(LoadScope, load) + + QML_BEGIN_INSTR(LoadRoot, load) + registers[instr->load.reg].setQObject(context->contextObject); + QML_END_INSTR(LoadRoot, load) + + QML_BEGIN_INSTR(LoadAttached, attached) + { + const Register &input = registers[instr->attached.reg]; + Register &output = registers[instr->attached.output]; + if (input.isUndefined()) + THROW_EXCEPTION(instr->attached.exceptionId); + + QObject *object = registers[instr->attached.reg].getQObject(); + if (!object) { + output.setUndefined(); + } else { + QObject *attached = qmlAttachedPropertiesObjectById(instr->attached.id, input.getQObject(), true); + Q_ASSERT(attached); + output.setQObject(attached); + } + } + QML_END_INSTR(LoadAttached, attached) + + QML_BEGIN_INSTR(UnaryNot, unaryop) + { + registers[instr->unaryop.output].setbool(!registers[instr->unaryop.src].getbool()); + } + QML_END_INSTR(UnaryNot, unaryop) + + QML_BEGIN_INSTR(UnaryMinusReal, unaryop) + { + registers[instr->unaryop.output].setqreal(-registers[instr->unaryop.src].getqreal()); + } + QML_END_INSTR(UnaryMinusReal, unaryop) + + QML_BEGIN_INSTR(UnaryMinusInt, unaryop) + { + registers[instr->unaryop.output].setint(-registers[instr->unaryop.src].getint()); + } + QML_END_INSTR(UnaryMinusInt, unaryop) + + QML_BEGIN_INSTR(UnaryPlusReal, unaryop) + { + registers[instr->unaryop.output].setqreal(+registers[instr->unaryop.src].getqreal()); + } + QML_END_INSTR(UnaryPlusReal, unaryop) + + QML_BEGIN_INSTR(UnaryPlusInt, unaryop) + { + registers[instr->unaryop.output].setint(+registers[instr->unaryop.src].getint()); + } + QML_END_INSTR(UnaryPlusInt, unaryop) + + QML_BEGIN_INSTR(ConvertBoolToInt, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setint(src.getbool()); + } + QML_END_INSTR(ConvertBoolToInt, unaryop) + + QML_BEGIN_INSTR(ConvertBoolToReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setqreal(src.getbool()); + } + QML_END_INSTR(ConvertBoolToReal, unaryop) + + QML_BEGIN_INSTR(ConvertBoolToString, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) { + output.setUndefined(); + } else { + new (output.getstringptr()) QString(QLatin1String(src.getbool() ? "true" : "false")); + STRING_REGISTER(instr->unaryop.output); + } + } + QML_END_INSTR(ConvertBoolToString, unaryop) + + QML_BEGIN_INSTR(ConvertIntToBool, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setbool(src.getint()); + } + QML_END_INSTR(ConvertIntToBool, unaryop) + + QML_BEGIN_INSTR(ConvertIntToReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setqreal(qreal(src.getint())); + } + QML_END_INSTR(ConvertIntToReal, unaryop) + + QML_BEGIN_INSTR(ConvertIntToString, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) { + output.setUndefined(); + } else { + new (output.getstringptr()) QString(QString::number(src.getint())); + STRING_REGISTER(instr->unaryop.output); + } + } + QML_END_INSTR(ConvertIntToString, unaryop) + + QML_BEGIN_INSTR(ConvertRealToBool, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setbool(src.getqreal() != 0); + } + QML_END_INSTR(ConvertRealToBool, unaryop) + + QML_BEGIN_INSTR(ConvertRealToInt, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setint(toInt32(src.getqreal())); + } + QML_END_INSTR(ConvertRealToInt, unaryop) + + QML_BEGIN_INSTR(ConvertRealToString, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + new (output.getstringptr()) QString(QString::number(src.getqreal())); + STRING_REGISTER(instr->unaryop.output); + } + } + QML_END_INSTR(ConvertRealToString, unaryop) + + QML_BEGIN_INSTR(ConvertStringToBool, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. + // Ideally we should just call the methods in the QScript namespace directly. + QScriptValue tmp(*src.getstringptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupString(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + output.setbool(tmp.toBool()); + } + } + QML_END_INSTR(ConvertStringToBool, unaryop) + + QML_BEGIN_INSTR(ConvertStringToInt, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. + // Ideally we should just call the methods in the QScript namespace directly. + QScriptValue tmp(*src.getstringptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupString(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + output.setint(tmp.toInt32()); + } + } + QML_END_INSTR(ConvertStringToInt, unaryop) + + QML_BEGIN_INSTR(ConvertStringToReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) { + output.setUndefined(); + } else { + // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. + // Ideally we should just call the methods in the QScript namespace directly. + QScriptValue tmp(*src.getstringptr()); + if (instr->unaryop.src == instr->unaryop.output) { + output.cleanupString(); + MARK_CLEAN_REGISTER(instr->unaryop.output); + } + output.setqreal(tmp.toNumber()); + } + } + QML_END_INSTR(ConvertStringToReal, unaryop) + + QML_BEGIN_INSTR(MathSinReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setqreal(qSin(src.getqreal())); + } + QML_END_INSTR(MathSinReal, unaryop) + + QML_BEGIN_INSTR(MathCosReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setqreal(qCos(src.getqreal())); + } + QML_END_INSTR(MathCosReal, unaryop) + + QML_BEGIN_INSTR(MathRoundReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setint(qRound(src.getqreal())); + } + QML_END_INSTR(MathRoundReal, unaryop) + + QML_BEGIN_INSTR(MathFloorReal, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (src.isUndefined()) output.setUndefined(); + else output.setint(qFloor(src.getqreal())); + } + QML_END_INSTR(MathFloorReal, unaryop) + + QML_BEGIN_INSTR(MathPIReal, unaryop) + { + static const qsreal qmlPI = 2.0 * qAsin(1.0); + Register &output = registers[instr->unaryop.output]; + output.setqreal(qmlPI); + } + QML_END_INSTR(MathPIReal, unaryop) + + QML_BEGIN_INSTR(Real, real_value) + registers[instr->real_value.reg].setqreal(instr->real_value.value); + QML_END_INSTR(Real, real_value) + + QML_BEGIN_INSTR(Int, int_value) + registers[instr->int_value.reg].setint(instr->int_value.value); + QML_END_INSTR(Int, int_value) + + QML_BEGIN_INSTR(Bool, bool_value) + registers[instr->bool_value.reg].setbool(instr->bool_value.value); + QML_END_INSTR(Bool, bool_value) + + QML_BEGIN_INSTR(String, string_value) + { + Register &output = registers[instr->string_value.reg]; + QChar *string = (QChar *)(data + instr->string_value.offset); + new (output.getstringptr()) QString(string, instr->string_value.length); + STRING_REGISTER(instr->string_value.reg); + } + QML_END_INSTR(String, string_value) + + QML_BEGIN_INSTR(EnableV4Test, string_value) + { + testBindingSource = new QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); + testBinding = true; + } + QML_END_INSTR(String, string_value) + + QML_BEGIN_INSTR(BitAndInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() & + registers[instr->binaryop.right].getint()); + } + QML_END_INSTR(BitAndInt, binaryop) + + QML_BEGIN_INSTR(BitOrInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() | + registers[instr->binaryop.right].getint()); + } + QML_END_INSTR(BitAndInt, binaryop) + + QML_BEGIN_INSTR(BitXorInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() ^ + registers[instr->binaryop.right].getint()); + } + QML_END_INSTR(BitXorInt, binaryop) + + QML_BEGIN_INSTR(AddReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() + + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(AddReal, binaryop) + + QML_BEGIN_INSTR(AddString, binaryop) + { + QString &string = *registers[instr->binaryop.output].getstringptr(); + if (instr->binaryop.output == instr->binaryop.left) { + string += registers[instr->binaryop.right].getstringptr(); + } else { + string = *registers[instr->binaryop.left].getstringptr() + + *registers[instr->binaryop.right].getstringptr(); + } + } + QML_END_INSTR(AddString, binaryop) + + QML_BEGIN_INSTR(SubReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() - + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(SubReal, binaryop) + + QML_BEGIN_INSTR(MulReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() * + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(MulReal, binaryop) + + QML_BEGIN_INSTR(DivReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() / + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(DivReal, binaryop) + + QML_BEGIN_INSTR(ModReal, binaryop) + { + Register &target = registers[instr->binaryop.output]; + const Register &left = registers[instr->binaryop.left]; + const Register &right = registers[instr->binaryop.right]; + if (QMetaType::QReal == QMetaType::Float) + target.setqreal(::fmodf(left.getqreal(), right.getqreal())); + else + target.setqreal(::fmod(left.getqreal(), right.getqreal())); + } + QML_END_INSTR(ModInt, binaryop) + + QML_BEGIN_INSTR(LShiftInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() << + registers[instr->binaryop.right].getint()); + } + QML_END_INSTR(LShiftInt, binaryop) + + QML_BEGIN_INSTR(RShiftInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() >> + registers[instr->binaryop.right].getint()); + } + QML_END_INSTR(RShiftInt, binaryop) + + QML_BEGIN_INSTR(URShiftInt, binaryop) + { + registers[instr->binaryop.output].setint((unsigned)registers[instr->binaryop.left].getint() >> + registers[instr->binaryop.right].getint()); + } + QML_END_INSTR(URShiftInt, binaryop) + + QML_BEGIN_INSTR(GtReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() > + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(GtReal, binaryop) + + QML_BEGIN_INSTR(LtReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() < + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(LtReal, binaryop) + + QML_BEGIN_INSTR(GeReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() >= + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(GeReal, binaryop) + + QML_BEGIN_INSTR(LeReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() <= + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(LeReal, binaryop) + + QML_BEGIN_INSTR(EqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() == + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(EqualReal, binaryop) + + QML_BEGIN_INSTR(NotEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() != + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(NotEqualReal, binaryop) + + QML_BEGIN_INSTR(StrictEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() == + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(StrictEqualReal, binaryop) + + QML_BEGIN_INSTR(StrictNotEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() != + registers[instr->binaryop.right].getqreal()); + } + QML_END_INSTR(StrictNotEqualReal, binaryop) + + QML_BEGIN_INSTR(GtString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a > b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_END_INSTR(GtString, binaryop) + + QML_BEGIN_INSTR(LtString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a < b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_END_INSTR(LtString, binaryop) + + QML_BEGIN_INSTR(GeString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a >= b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_END_INSTR(GeString, binaryop) + + QML_BEGIN_INSTR(LeString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a <= b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_END_INSTR(LeString, binaryop) + + QML_BEGIN_INSTR(EqualString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a == b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_END_INSTR(EqualString, binaryop) + + QML_BEGIN_INSTR(NotEqualString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a != b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_END_INSTR(NotEqualString, binaryop) + + QML_BEGIN_INSTR(StrictEqualString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a == b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_END_INSTR(StrictEqualString, binaryop) + + QML_BEGIN_INSTR(StrictNotEqualString, binaryop) + { + const QString &a = *registers[instr->binaryop.left].getstringptr(); + const QString &b = *registers[instr->binaryop.right].getstringptr(); + bool result = a != b; + if (instr->binaryop.left == instr->binaryop.output) { + registers[instr->binaryop.output].cleanupString(); + MARK_CLEAN_REGISTER(instr->binaryop.output); + } + registers[instr->binaryop.output].setbool(result); + } + QML_END_INSTR(StrictNotEqualString, binaryop) + + QML_BEGIN_INSTR(NewString, construct) + { + Register &output = registers[instr->construct.reg]; + new (output.getstringptr()) QString; + STRING_REGISTER(instr->construct.reg); + } + QML_END_INSTR(NewString, construct) + + QML_BEGIN_INSTR(NewUrl, construct) + { + Register &output = registers[instr->construct.reg]; + new (output.geturlptr()) QUrl; + URL_REGISTER(instr->construct.reg); + } + QML_END_INSTR(NewUrl, construct) + + QML_BEGIN_INSTR(Fetch, fetch) + { + Register ® = registers[instr->fetch.reg]; + + if (reg.isUndefined()) + THROW_EXCEPTION(instr->fetch.exceptionId); + + QObject *object = reg.getQObject(); + if (!object) { + THROW_EXCEPTION(instr->fetch.exceptionId); + } else { + reg.init((Register::Type)instr->fetch.valueType); + if (instr->fetch.valueType >= FirstCleanupType) + MARK_REGISTER(instr->fetch.reg); + void *argv[] = { reg.typeDataPtr(), 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); + } + } + QML_END_INSTR(Fetch, fetch) + + QML_BEGIN_INSTR(TestV4Store, storetest) + { + Register &data = registers[instr->storetest.reg]; + testBindingResult(*testBindingSource, bindingLine, bindingColumn, context, + scope, data, instr->storetest.regType); + } + QML_END_INSTR(TestV4Store, storetest) + + QML_BEGIN_INSTR(Store, store) + { + Register &data = registers[instr->store.reg]; + + if (data.isUndefined()) + THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign undefined value")); + + int status = -1; + void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; + QMetaObject::metacall(output, QMetaObject::WriteProperty, + instr->store.index, argv); + + goto programExit; + } + QML_END_INSTR(Store, store) + + QML_BEGIN_INSTR(Copy, copy) + registers[instr->copy.reg].copy(registers[instr->copy.src]); + if (registers[instr->copy.reg].gettype() >= FirstCleanupType) + MARK_REGISTER(instr->copy.reg); + QML_END_INSTR(Copy, copy) + + QML_BEGIN_INSTR(Jump, jump) + if (instr->jump.reg == -1 || !registers[instr->jump.reg].getbool()) + code += instr->jump.count; + QML_END_INSTR(Jump, jump) + + QML_BEGIN_INSTR(BranchTrue, branchop) + if (registers[instr->branchop.reg].getbool()) + code += instr->branchop.offset; + QML_END_INSTR(BranchTrue, branchop) + + QML_BEGIN_INSTR(BranchFalse, branchop) + if (! registers[instr->branchop.reg].getbool()) + code += instr->branchop.offset; + QML_END_INSTR(BranchFalse, branchop) + + QML_BEGIN_INSTR(Branch, branchop) + code += instr->branchop.offset; + QML_END_INSTR(Branch, branchop) + + QML_BEGIN_INSTR(Block, blockop) + executedBlocks |= instr->blockop.block; + QML_END_INSTR(Block, blockop) + + QML_BEGIN_INSTR(InitString, 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, initstring) + + QML_BEGIN_INSTR(CleanupRegister, cleanup) + registers[instr->cleanup.reg].cleanup(); + QML_END_INSTR(CleanupRegister, cleanup) + +#ifdef QML_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QDeclarativeV4: Unknown instruction %d encountered.", instr->common.type); + break; + } // switch + + } // while +#endif + + Q_ASSERT(!"Unreachable code reached"); + +programExit: +exceptionExit: + delete testBindingSource; + + int reg = 0; + while (cleanupRegisterMask) { + if (cleanupRegisterMask & 0x1) + registers[reg].cleanup(); + + reg++; + cleanupRegisterMask >>= 1; + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v4/qdeclarativev4bindings_p.h b/src/declarative/qml/v4/qdeclarativev4bindings_p.h new file mode 100644 index 0000000000..9f225b65b6 --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4bindings_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEV4BINDINGS_P_H +#define QDECLARATIVEV4BINDINGS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qdeclarativeexpression_p.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativev4instruction_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeV4BindingsPrivate; +class QDeclarativeV4Bindings : public QObject, + public QDeclarativeAbstractExpression, + public QDeclarativeRefCount +{ +public: + QDeclarativeV4Bindings(const char *program, QDeclarativeContextData *context); + virtual ~QDeclarativeV4Bindings(); + + QDeclarativeAbstractBinding *configBinding(int index, QObject *target, QObject *scope, int property); + +#ifdef QML_THREADED_INTERPRETER + static void **getDecodeInstrTable(); +#endif + +protected: + int qt_metacall(QMetaObject::Call, int, void **); + +private: + Q_DISABLE_COPY(QDeclarativeV4Bindings) + Q_DECLARE_PRIVATE(QDeclarativeV4Bindings) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEV4BINDINGS_P_H + diff --git a/src/declarative/qml/v4/qdeclarativev4compiler.cpp b/src/declarative/qml/v4/qdeclarativev4compiler.cpp new file mode 100644 index 0000000000..a7eecce0c9 --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4compiler.cpp @@ -0,0 +1,1340 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativev4compiler_p.h" +#include "qdeclarativev4compiler_p_p.h" +#include "qdeclarativev4program_p.h" +#include "qdeclarativev4ir_p.h" +#include "qdeclarativev4irbuilder_p.h" + +#include <private/qdeclarativejsast_p.h> +#include <private/qdeclarativefastproperties_p.h> +#include <private/qdeclarativejsengine_p.h> +#include <private/qdeclarativeanchors_p_p.h> // For AnchorLine +#include <private/qsganchors_p_p.h> // For AnchorLine + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP) +DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER) +DEFINE_BOOL_CONFIG_OPTION(qmlExperimental, QML_EXPERIMENTAL) +DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER) +DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST) + +Q_GLOBAL_STATIC(QDeclarativeFastProperties, fastProperties) + +static bool qmlBindingsTest = false; + +using namespace QDeclarativeJS; +QDeclarativeV4CompilerPrivate::QDeclarativeV4CompilerPrivate() +: _function(0) , _block(0) , _discarded(false) +{ +} + +// +// tracing +// +void QDeclarativeV4CompilerPrivate::trace(int line, int column) +{ + bytecode.clear(); + + this->currentReg = _function->tempCount; + + foreach (IR::BasicBlock *bb, _function->basicBlocks) { + if (! bb->isTerminated() && (bb->index + 1) < _function->basicBlocks.size()) + bb->JUMP(_function->basicBlocks.at(bb->index + 1)); + } + + QVector<IR::BasicBlock *> blocks; + trace(&blocks); + currentBlockMask = 0x00000001; + + + for (int i = 0; i < blocks.size(); ++i) { + IR::BasicBlock *block = blocks.at(i); + IR::BasicBlock *next = i + 1 < blocks.size() ? blocks.at(i + 1) : 0; + if (IR::Stmt *terminator = block->terminator()) { + if (IR::CJump *cj = terminator->asCJump()) { + if (cj->iffalse != next) { + block->i(new IR::Jump(cj->iffalse)); + } + } else if (IR::Jump *j = terminator->asJump()) { + if (j->target == next) { + delete block->statements.back(); + block->statements.resize(block->statements.size() - 1); + } + } + } + + block->offset = bytecode.size(); + + if (bytecode.isEmpty()) { + if (qmlBindingsTest || bindingsDump()) { + Instr id; + id.common.type = Instr::BindingId; + id.id.column = column; + id.id.line = line; + gen(id); + } + + if (qmlBindingsTest) { + QString str = expression->expression.asScript(); + QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); + int offset = data.count(); + data += strdata; + + Instr test; + test.common.type = Instr::EnableV4Test; + test.string_value.reg = 0; + test.string_value.offset = offset; + test.string_value.length = str.length(); + gen(test); + } + } + + bool usic = false; + int patchesCount = patches.count(); + qSwap(usedSubscriptionIdsChanged, usic); + + int blockopIndex = bytecode.size(); + Instr blockop; + blockop.block(currentBlockMask); + gen(blockop); + + foreach (IR::Stmt *s, block->statements) + s->accept(this); + + qSwap(usedSubscriptionIdsChanged, usic); + + if (usic) { + if (currentBlockMask == 0x80000000) { + discard(); + return; + } + currentBlockMask <<= 1; + } else { + const int adjust = bytecode.remove(blockopIndex); + // Correct patches + for (int ii = patchesCount; ii < patches.count(); ++ii) + patches[ii].offset -= adjust; + } + } + +#ifdef DEBUG_IR_STRUCTURE + IR::IRDump dump; + for (int i = 0; i < blocks.size(); ++i) { + dump.basicblock(blocks.at(i)); + } +#endif + + + // back patching + foreach (const Patch &patch, patches) { + Instr &instr = bytecode[patch.offset]; + instr.branchop.offset = patch.block->offset - patch.offset - instr.size(); + } + + patches.clear(); +} + +void QDeclarativeV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks) +{ + QList<IR::BasicBlock *> todo = QList<IR::BasicBlock *>::fromVector(_function->basicBlocks); + while (! todo.isEmpty()) { + IR::BasicBlock *block = todo.takeFirst(); + + while (! blocks->contains(block)) { + blocks->append(block); + + if (IR::Stmt *terminator = block->terminator()) { + if (IR::CJump *cj = terminator->asCJump()) + block = cj->iffalse; + else if (IR::Jump *j = terminator->asJump()) + block = j->target; + } + } + } +} + +void QDeclarativeV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r) +{ + if (!e) { + discard(); + } else { + qSwap(currentReg, r); + e->accept(this); + qSwap(currentReg, r); + } +} + +// +// expressions +// +void QDeclarativeV4CompilerPrivate::visitConst(IR::Const *e) +{ + Instr i; + switch (e->type) { + case IR::BoolType: + i.move_reg_bool(currentReg, e->value); + gen(i); + break; + + case IR::IntType: + i.move_reg_int(currentReg, e->value); + gen(i); + break; + + case IR::RealType: + i.move_reg_qreal(currentReg, e->value); + gen(i); + break; + + default: + if (qmlVerboseCompiler()) + qWarning() << Q_FUNC_INFO << "unexpected type"; + discard(); + } +} + +void QDeclarativeV4CompilerPrivate::visitString(IR::String *e) +{ + registerLiteralString(currentReg, e->value); +} + +void QDeclarativeV4CompilerPrivate::visitName(IR::Name *e) +{ + if (e->base) { + // fetch the object and store it in reg. + traceExpression(e->base, currentReg); + } else { + _subscribeName.clear(); + } + + if (e->storage == IR::Name::RootStorage) { + + Instr instr; + instr.load_root(currentReg); + gen(instr); + + if (e->symbol == IR::Name::IdObject) { + // The ID is a reference to the root object + return; + } + + } else if (e->storage == IR::Name::ScopeStorage) { + + Instr instr; + instr.load_scope(currentReg); + gen(instr); + + _subscribeName << contextName(); + + } else if (e->storage == IR::Name::IdStorage) { + + Instr instr; + instr.load_id(currentReg, e->index); + gen(instr); + + _subscribeName << QLatin1String("$$$ID_") + e->id; + + if (blockNeedsSubscription(_subscribeName)) { + Instr sub; + sub.subscribeId(currentReg, subscriptionIndex(_subscribeName), instr.load.index); + gen(sub); + } + + return; + } else { + // No action needed + } + + switch (e->symbol) { + case IR::Name::Unbound: + case IR::Name::IdObject: + case IR::Name::Slot: + case IR::Name::Object: { + Q_ASSERT(!"Unreachable"); + discard(); + } break; + + case IR::Name::AttachType: { + _subscribeName << e->id; + + Instr attached; + attached.common.type = Instr::LoadAttached; + attached.attached.output = currentReg; + attached.attached.reg = currentReg; + attached.attached.exceptionId = exceptionId(e->line, e->column); + Q_ASSERT(e->declarativeType->attachedPropertiesId() != -1); + attached.attached.id = e->declarativeType->attachedPropertiesId(); + gen(attached); + } break; + + case IR::Name::Property: { + _subscribeName << e->id; + + QMetaProperty prop = e->meta->property(e->index); + int fastFetchIndex = fastProperties()->accessorIndexForProperty(e->meta, e->index); + + const int propTy = prop.userType(); + QDeclarativeRegisterType regType; + + switch (propTy) { + case QMetaType::QReal: + regType = QRealType; + break; + case QMetaType::Bool: + regType = BoolType; + break; + case QMetaType::Int: + regType = IntType; + break; + case QMetaType::QString: + regType = QStringType; + break; + + default: + if (propTy == qMetaTypeId<QDeclarativeAnchorLine>()) { + regType = PODValueType; + } else if (propTy == qMetaTypeId<QSGAnchorLine>()) { + regType = PODValueType; + } else if (QDeclarativeMetaType::isQObject(propTy)) { + regType = QObjectStarType; + } else { + if (qmlVerboseCompiler()) + qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy); + discard(); // Unsupported type + return; + } + + break; + } // switch + + Instr fetch; + + if (fastFetchIndex != -1) { + fetch.common.type = Instr::FetchAndSubscribe; + fetch.fetchAndSubscribe.reg = currentReg; + fetch.fetchAndSubscribe.function = fastFetchIndex; + fetch.fetchAndSubscribe.subscription = subscriptionIndex(_subscribeName); + fetch.fetchAndSubscribe.exceptionId = exceptionId(e->line, e->column); + fetch.fetchAndSubscribe.valueType = regType; + } else { + if (blockNeedsSubscription(_subscribeName) && prop.hasNotifySignal() && prop.notifySignalIndex() != -1) { + Instr sub; + sub.subscribe(currentReg, subscriptionIndex(_subscribeName), prop.notifySignalIndex()); + gen(sub); + } + + fetch.common.type = Instr::Fetch; + fetch.fetch.reg = currentReg; + fetch.fetch.index = e->index; + fetch.fetch.exceptionId = exceptionId(e->line, e->column); + fetch.fetch.valueType = regType; + } + + gen(fetch); + + } break; + } // switch +} + +void QDeclarativeV4CompilerPrivate::visitTemp(IR::Temp *e) +{ + if (currentReg != e->index) { + Instr i; + i.move_reg_reg(currentReg, e->index); + gen(i); + } +} + +void QDeclarativeV4CompilerPrivate::visitUnop(IR::Unop *e) +{ + Instr i; + + quint8 src = currentReg; + + if (IR::Temp *temp = e->expr->asTemp()) { + src = temp->index; + } else { + traceExpression(e->expr, src); + } + + switch (e->op) { + case IR::OpInvalid: + Q_ASSERT(!"unreachable"); + break; + + case IR::OpIfTrue: + if (src != currentReg) { + i.move_reg_reg(currentReg, src); + gen(i); + } else { + // nothing to do + } + break; + + case IR::OpNot: + i.unary_not(currentReg, src); + gen(i); + break; + + case IR::OpUMinus: + if (e->expr->type == IR::RealType) { + i.uminus_real(currentReg, src); + gen(i); + } else if (e->expr->type == IR::IntType) { + convertToReal(e->expr, currentReg); + i.uminus_real(currentReg, src); + gen(i); + } else { + discard(); + } + break; + + case IR::OpUPlus: + if (e->expr->type == IR::RealType) { + i.uplus_real(currentReg, src); + gen(i); + } else if (e->expr->type == IR::IntType) { + convertToReal(e->expr, currentReg); + i.uplus_real(currentReg, src); + gen(i); + } else { + discard(); + } + break; + + case IR::OpCompl: + i.ucompl_real(currentReg, src); + gen(i); + discard(); // ??? + break; + + case IR::OpBitAnd: + case IR::OpBitOr: + case IR::OpBitXor: + case IR::OpAdd: + case IR::OpSub: + case IR::OpMul: + case IR::OpDiv: + case IR::OpMod: + case IR::OpLShift: + case IR::OpRShift: + case IR::OpURShift: + case IR::OpGt: + case IR::OpLt: + case IR::OpGe: + case IR::OpLe: + case IR::OpEqual: + case IR::OpNotEqual: + case IR::OpStrictEqual: + case IR::OpStrictNotEqual: + case IR::OpAnd: + case IR::OpOr: + Q_ASSERT(!"unreachable"); + break; + } // switch +} + +void QDeclarativeV4CompilerPrivate::convertToReal(IR::Expr *expr, int reg) +{ + if (expr->type == IR::RealType) + return; + + Instr conv; + conv.unaryop.output = reg; + conv.unaryop.src = reg; + + switch (expr->type) { + case IR::BoolType: + conv.common.type = Instr::ConvertBoolToReal; + gen(conv); + break; + + case IR::IntType: + conv.common.type = Instr::ConvertIntToReal; + gen(conv); + break; + + case IR::RealType: + // nothing to do + return; + + default: + discard(); + break; + } // switch +} + +void QDeclarativeV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg) +{ + if (expr->type == IR::IntType) + return; + + Instr conv; + conv.unaryop.output = reg; + conv.unaryop.src = reg; + + switch (expr->type) { + case IR::BoolType: + conv.common.type = Instr::ConvertBoolToInt; + gen(conv); + break; + + case IR::IntType: + // nothing to do + return; + + case IR::RealType: + conv.common.type = Instr::ConvertRealToInt; + gen(conv); + break; + + default: + discard(); + break; + } // switch +} + +void QDeclarativeV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg) +{ + if (expr->type == IR::BoolType) + return; + + Instr conv; + conv.unaryop.output = reg; + conv.unaryop.src = reg; + + switch (expr->type) { + case IR::BoolType: + // nothing to do + break; + + case IR::IntType: + conv.common.type = Instr::ConvertIntToBool; + gen(conv); + break; + + case IR::RealType: + conv.common.type = Instr::ConvertRealToBool; + gen(conv); + return; + + case IR::StringType: + conv.common.type = Instr::ConvertStringToBool; + gen(conv); + return; + + default: + discard(); + break; + } // switch +} + +quint8 QDeclarativeV4CompilerPrivate::instructionOpcode(IR::Binop *e) +{ + switch (e->op) { + case IR::OpInvalid: + return Instr::Noop; + + case IR::OpIfTrue: + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + return Instr::Noop; + + case IR::OpBitAnd: + return Instr::BitAndInt; + + case IR::OpBitOr: + return Instr::BitOrInt; + + case IR::OpBitXor: + return Instr::BitXorInt; + + case IR::OpAdd: + if (e->type == IR::StringType) + return Instr::AddString; + return Instr::AddReal; + + case IR::OpSub: + return Instr::SubReal; + + case IR::OpMul: + return Instr::MulReal; + + case IR::OpDiv: + return Instr::DivReal; + + case IR::OpMod: + return Instr::ModReal; + + case IR::OpLShift: + return Instr::LShiftInt; + + case IR::OpRShift: + return Instr::RShiftInt; + + case IR::OpURShift: + return Instr::URShiftInt; + + case IR::OpGt: + if (e->left->type == IR::StringType) + return Instr::GtString; + return Instr::GtReal; + + case IR::OpLt: + if (e->left->type == IR::StringType) + return Instr::LtString; + return Instr::LtReal; + + case IR::OpGe: + if (e->left->type == IR::StringType) + return Instr::GeString; + return Instr::GeReal; + + case IR::OpLe: + if (e->left->type == IR::StringType) + return Instr::LeString; + return Instr::LeReal; + + case IR::OpEqual: + if (e->left->type == IR::StringType) + return Instr::EqualString; + return Instr::EqualReal; + + case IR::OpNotEqual: + if (e->left->type == IR::StringType) + return Instr::NotEqualString; + return Instr::NotEqualReal; + + case IR::OpStrictEqual: + if (e->left->type == IR::StringType) + return Instr::StrictEqualString; + return Instr::StrictEqualReal; + + case IR::OpStrictNotEqual: + if (e->left->type == IR::StringType) + return Instr::StrictNotEqualString; + return Instr::StrictNotEqualReal; + + case IR::OpAnd: + case IR::OpOr: + return Instr::Noop; + + } // switch + + return Instr::Noop; +} + +void QDeclarativeV4CompilerPrivate::visitBinop(IR::Binop *e) +{ + int left = currentReg; + int right = currentReg + 1; + + if (e->left->asTemp() && e->type != IR::StringType) // Not sure if the e->type != String test is needed + left = e->left->asTemp()->index; + else + traceExpression(e->left, left); + + if (IR::Temp *t = e->right->asTemp()) + right = t->index; + else + traceExpression(e->right, right); + + if (e->left->type != e->right->type) { + if (qmlVerboseCompiler()) + qWarning().nospace() << "invalid operands to binary operator " << IR::binaryOperator(e->op) + << "(`" << IR::binaryOperator(e->left->type) + << "' and `" + << IR::binaryOperator(e->right->type) + << "'"; + discard(); + return; + } + + switch (e->op) { + case IR::OpInvalid: + discard(); + break; + + // unary + case IR::OpIfTrue: + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + discard(); + break; + + case IR::OpBitAnd: + case IR::OpBitOr: + case IR::OpBitXor: + case IR::OpLShift: + case IR::OpRShift: + case IR::OpURShift: + convertToInt(e->left, left); + convertToInt(e->right, right); + break; + + case IR::OpAdd: + if (e->type != IR::StringType) { + convertToReal(e->left, left); + convertToReal(e->right, right); + } + break; + + case IR::OpSub: + case IR::OpMul: + case IR::OpDiv: + case IR::OpMod: + convertToReal(e->left, left); + convertToReal(e->right, right); + break; + + case IR::OpGt: + case IR::OpLt: + case IR::OpGe: + case IR::OpLe: + case IR::OpEqual: + case IR::OpNotEqual: + case IR::OpStrictEqual: + case IR::OpStrictNotEqual: + if (e->left->type != IR::StringType) { + convertToReal(e->left, left); + convertToReal(e->right, right); + } + break; + + case IR::OpAnd: + case IR::OpOr: + discard(); // ### unreachable + break; + } // switch + + const quint8 opcode = instructionOpcode(e); + if (opcode != Instr::Noop) { + Instr instr; + instr.common.type = opcode; + instr.binaryop.output = currentReg; + instr.binaryop.left = left; + instr.binaryop.right = right; + gen(instr); + } +} + +void QDeclarativeV4CompilerPrivate::visitCall(IR::Call *call) +{ + if (IR::Name *name = call->base->asName()) { + if (call->args.size() == 1 && call->args.at(0)->type == IR::RealType) { + traceExpression(call->args.at(0), currentReg); + + Instr instr; + instr.common.type = Instr::Noop; + + switch (name->builtin) { + case IR::NoBuiltinSymbol: + break; + + case IR::MathSinBultinFunction: + instr.math_sin_real(currentReg); + break; + + case IR::MathCosBultinFunction: + instr.math_cos_real(currentReg); + break; + + case IR::MathRoundBultinFunction: + instr.math_round_real(currentReg); + break; + + case IR::MathFloorBultinFunction: + instr.math_floor_real(currentReg); + break; + + case IR::MathPIBuiltinConstant: + break; + } // switch + + if (instr.common.type != Instr::Noop) { + gen(instr); + return; + } + } + } + + if (qmlVerboseCompiler()) + qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__; + discard(); +} + + +// +// statements +// +void QDeclarativeV4CompilerPrivate::visitExp(IR::Exp *s) +{ + traceExpression(s->expr, currentReg); +} + +void QDeclarativeV4CompilerPrivate::visitMove(IR::Move *s) +{ + IR::Temp *target = s->target->asTemp(); + Q_ASSERT(target != 0); + + quint8 dest = target->index; + + if (target->type != s->source->type) { + quint8 src = dest; + + if (IR::Temp *t = s->source->asTemp()) + src = t->index; + else + traceExpression(s->source, dest); + + Instr conv; + conv.common.type = Instr::Noop; + if (target->type == IR::BoolType) { + switch (s->source->type) { + case IR::IntType: conv.common.type = Instr::ConvertIntToBool; break; + case IR::RealType: conv.common.type = Instr::ConvertRealToBool; break; + case IR::StringType: conv.common.type = Instr::ConvertStringToBool; break; + default: break; + } // switch + } else if (target->type == IR::IntType) { + switch (s->source->type) { + case IR::BoolType: conv.common.type = Instr::ConvertBoolToInt; break; + case IR::RealType: { + if (s->isMoveForReturn) + conv.common.type = Instr::MathRoundReal; + else + conv.common.type = Instr::ConvertRealToInt; + break; + } + case IR::StringType: conv.common.type = Instr::ConvertStringToInt; break; + default: break; + } // switch + } else if (target->type == IR::RealType) { + switch (s->source->type) { + case IR::BoolType: conv.common.type = Instr::ConvertBoolToReal; break; + case IR::IntType: conv.common.type = Instr::ConvertIntToReal; break; + case IR::StringType: conv.common.type = Instr::ConvertStringToReal; break; + default: break; + } // switch + } else if (target->type == IR::StringType) { + switch (s->source->type) { + case IR::BoolType: conv.common.type = Instr::ConvertBoolToString; break; + case IR::IntType: conv.common.type = Instr::ConvertIntToString; break; + case IR::RealType: conv.common.type = Instr::ConvertRealToString; break; + default: break; + } // switch + } + if (conv.common.type != Instr::Noop) { + conv.unaryop.output = dest; + conv.unaryop.src = src; + gen(conv); + } else { + discard(); + } + } else { + traceExpression(s->source, dest); + } +} + +void QDeclarativeV4CompilerPrivate::visitJump(IR::Jump *s) +{ + patches.append(Patch(s->target, bytecode.size())); + + Instr i; + i.branch(0); // ### backpatch + gen(i); +} + +void QDeclarativeV4CompilerPrivate::visitCJump(IR::CJump *s) +{ + traceExpression(s->cond, currentReg); + + patches.append(Patch(s->iftrue, bytecode.size())); + + Instr i; + i.branch_true(currentReg, 0); // ### backpatch + gen(i); +} + +void QDeclarativeV4CompilerPrivate::visitRet(IR::Ret *s) +{ + Q_ASSERT(s->expr != 0); + + int storeReg = currentReg; + + if (IR::Temp *temp = s->expr->asTemp()) { + storeReg = temp->index; + } else { + traceExpression(s->expr, storeReg); + } + + if (qmlBindingsTest) { + Instr test; + test.common.type = Instr::TestV4Store; + test.storetest.reg = storeReg; + switch (s->type) { + case IR::StringType: + test.storetest.regType = QMetaType::QString; + break; + case IR::UrlType: + test.storetest.regType = QMetaType::QUrl; + break; + case IR::AnchorLineType: + test.storetest.regType = qMetaTypeId<QDeclarativeAnchorLine>(); + break; + case IR::SGAnchorLineType: + test.storetest.regType = qMetaTypeId<QSGAnchorLine>(); + break; + case IR::ObjectType: + test.storetest.regType = QMetaType::QObjectStar; + break; + case IR::BoolType: + test.storetest.regType = QMetaType::Bool; + break; + case IR::IntType: + test.storetest.regType = QMetaType::Int; + break; + case IR::RealType: + test.storetest.regType = QMetaType::QReal; + break; + default: + discard(); + return; + } + gen(test); + } + + Instr store; + store.common.type = Instr::Store; + store.store.output = 0; + store.store.index = expression->property->index; + store.store.reg = storeReg; + store.store.exceptionId = exceptionId(s->line, s->column); + gen(store); +} + +void QDeclarativeV4Compiler::dump(const QByteArray &programData) +{ + const QDeclarativeV4Program *program = (const QDeclarativeV4Program *)programData.constData(); + + qWarning() << "Program.bindings:" << program->bindings; + qWarning() << "Program.dataLength:" << program->dataLength; + qWarning() << "Program.subscriptions:" << program->subscriptions; + qWarning() << "Program.indentifiers:" << program->identifiers; + + const int programSize = program->instructionCount; + const char *start = program->instructions(); + const char *code = start; + const char *end = code + programSize; + while (code < end) { + Instr *instr = (Instr *) code; + instr->dump(code - start); + code += instr->size(); + } +} + +QDeclarativeFastProperties *QDeclarativeV4Compiler::fastPropertyAccessor() +{ + return fastProperties(); +} + +/*! +Clear the state associated with attempting to compile a specific binding. +This does not clear the global "committed binding" states. +*/ +void QDeclarativeV4CompilerPrivate::resetInstanceState() +{ + registerCleanups.clear(); + data = committed.data; + exceptions = committed.exceptions; + usedSubscriptionIds.clear(); + subscriptionIds = committed.subscriptionIds; + registeredStrings = committed.registeredStrings; + bytecode.clear(); + patches.clear(); + currentReg = 0; +} + +/*! +Mark the last compile as successful, and add it to the "committed data" +section. + +Returns the index for the committed binding. +*/ +int QDeclarativeV4CompilerPrivate::commitCompile() +{ + int rv = committed.count(); + committed.offsets << committed.bytecode.count(); + committed.dependencies << usedSubscriptionIds; + committed.bytecode += bytecode.code(); + committed.data = data; + committed.exceptions = exceptions; + committed.subscriptionIds = subscriptionIds; + committed.registeredStrings = registeredStrings; + return rv; +} + +bool QDeclarativeV4CompilerPrivate::compile(QDeclarativeJS::AST::Node *node) +{ + resetInstanceState(); + + if (expression->property->type == -1) + return false; + + AST::SourceLocation location; + if (AST::ExpressionNode *astExpression = node->expressionCast()) { + location = astExpression->firstSourceLocation(); + } else if (AST::Statement *astStatement = node->statementCast()) { + if (AST::Block *block = AST::cast<AST::Block *>(astStatement)) + location = block->lbraceToken; + else if (AST::IfStatement *ifStmt = AST::cast<AST::IfStatement *>(astStatement)) + location = ifStmt->ifToken; + else + return false; + } else { + return false; + } + + IR::Module module; + IR::Function *function = 0; + + QDeclarativeV4IRBuilder irBuilder(expression, engine); + if (!(function = irBuilder(&module, node))) + return false; + + bool discarded = false; + qSwap(_discarded, discarded); + qSwap(_function, function); + trace(location.startLine, location.startColumn); + qSwap(_function, function); + qSwap(_discarded, discarded); + + if (qmlVerboseCompiler()) { + QTextStream qerr(stderr, QIODevice::WriteOnly); + if (discarded) + qerr << "======== TODO ====== " << endl; + else + qerr << "==================== " << endl; + qerr << "\tline: " << location.startLine + << "\tcolumn: " << location.startColumn + << endl; + foreach (IR::BasicBlock *bb, function->basicBlocks) + bb->dump(qerr); + qerr << endl; + } + + if (discarded || subscriptionIds.count() > 0xFFFF || registeredStrings.count() > 0xFFFF) + return false; + + return true; +} + +// Returns a reg +int QDeclarativeV4CompilerPrivate::registerLiteralString(quint8 reg, const QString &str) +{ + // ### string cleanup + + QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); + int offset = data.count(); + data += strdata; + + Instr string; + string.common.type = Instr::String; + string.string_value.reg = reg; + string.string_value.offset = offset; + string.string_value.length = str.length(); + gen(string); + + return reg; +} + +// Returns an identifier offset +int QDeclarativeV4CompilerPrivate::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; + gen(reg); + return reg.initstring.offset; +} + +/*! +Returns true if the current expression has not already subscribed to \a sub in currentBlockMask. +*/ +bool QDeclarativeV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub) +{ + QString str = sub.join(QLatin1String(".")); + + QHash<QString, int>::ConstIterator iter = subscriptionIds.find(str); + if (iter == subscriptionIds.end()) + return true; + + QHash<int, quint32>::ConstIterator uiter = usedSubscriptionIds.find(*iter); + if (uiter == usedSubscriptionIds.end()) + return true; + else + return !(*uiter & currentBlockMask); +} + +int QDeclarativeV4CompilerPrivate::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()); + if (!(usedSubscriptionIds[*iter] & currentBlockMask)) { + usedSubscriptionIds[*iter] |= currentBlockMask; + usedSubscriptionIdsChanged = true; + } + return *iter; +} + +quint32 QDeclarativeV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub) +{ + QString str = sub.join(QLatin1String(".")); + + QHash<QString, int>::ConstIterator iter = subscriptionIds.find(str); + Q_ASSERT(iter != subscriptionIds.end()); + + QHash<int, quint32>::ConstIterator uiter = usedSubscriptionIds.find(*iter); + Q_ASSERT(uiter != usedSubscriptionIds.end()); + + return *uiter; +} + +quint8 QDeclarativeV4CompilerPrivate::exceptionId(quint32 line, quint32 column) +{ + quint8 rv = 0xFF; + if (exceptions.count() < 0xFF) { + rv = (quint8)exceptions.count(); + quint64 e = line; + e <<= 32; + e |= column; + exceptions.append(e); + } + return rv; +} + +quint8 QDeclarativeV4CompilerPrivate::exceptionId(QDeclarativeJS::AST::ExpressionNode *n) +{ + quint8 rv = 0xFF; + if (n && exceptions.count() < 0xFF) { + QDeclarativeJS::AST::SourceLocation l = n->firstSourceLocation(); + rv = exceptionId(l.startLine, l.startColumn); + } + return rv; +} + +QDeclarativeV4Compiler::QDeclarativeV4Compiler() +: d(new QDeclarativeV4CompilerPrivate) +{ + qmlBindingsTest |= qmlBindingsTestEnv(); +} + +QDeclarativeV4Compiler::~QDeclarativeV4Compiler() +{ + delete d; d = 0; +} + +/* +Returns true if any bindings were compiled. +*/ +bool QDeclarativeV4Compiler::isValid() const +{ + return !d->committed.bytecode.isEmpty(); +} + +/* +-1 on failure, otherwise the binding index to use. +*/ +int QDeclarativeV4Compiler::compile(const Expression &expression, QDeclarativeEnginePrivate *engine) +{ + if (!expression.expression.asAST()) return false; + + if (!qmlExperimental() && expression.property->isValueTypeSubProperty) + return -1; + + if (qmlDisableOptimizer()) + return -1; + + d->expression = &expression; + d->engine = engine; + + if (d->compile(expression.expression.asAST())) { + return d->commitCompile(); + } else { + return -1; + } +} + +QByteArray QDeclarativeV4CompilerPrivate::buildSignalTable() const +{ + QHash<int, QList<QPair<int, quint32> > > table; + + for (int ii = 0; ii < committed.count(); ++ii) { + const QHash<int, quint32> &deps = committed.dependencies.at(ii); + for (QHash<int, quint32>::ConstIterator iter = deps.begin(); iter != deps.end(); ++iter) + table[iter.key()].append(qMakePair(ii, iter.value())); + } + + QVector<quint32> header; + QVector<quint32> data; + for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { + header.append(committed.subscriptionIds.count() + data.count()); + const QList<QPair<int, quint32> > &bindings = table[ii]; + data.append(bindings.count()); + for (int jj = 0; jj < bindings.count(); ++jj) { + data.append(bindings.at(jj).first); + data.append(bindings.at(jj).second); + } + } + header << data; + + return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32)); +} + +QByteArray QDeclarativeV4CompilerPrivate::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 QDeclarativeV4Compiler::program() const +{ + QByteArray programData; + + if (isValid()) { + QDeclarativeV4Program prog; + prog.bindings = d->committed.count(); + + Bytecode bc; + Instr jump; + jump.common.type = Instr::Jump; + jump.jump.reg = -1; + + for (int ii = 0; ii < d->committed.count(); ++ii) { + jump.jump.count = d->committed.count() - ii - 1; + jump.jump.count*= jump.size(); + jump.jump.count+= d->committed.offsets.at(ii); + bc.append(jump); + } + + + QByteArray bytecode = bc.code(); + 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(); + int size = sizeof(QDeclarativeV4Program) + bytecode.count(); + size += prog.dataLength; + + programData.resize(size); + memcpy(programData.data(), &prog, sizeof(QDeclarativeV4Program)); + if (prog.dataLength) + memcpy((char *)((QDeclarativeV4Program *)programData.data())->data(), data.constData(), + data.size()); + memcpy((char *)((QDeclarativeV4Program *)programData.data())->instructions(), bytecode.constData(), + bytecode.count()); + } + + if (bindingsDump()) { + qWarning().nospace() << "Subscription slots:"; + + for (QHash<QString, int>::ConstIterator iter = d->committed.subscriptionIds.begin(); + iter != d->committed.subscriptionIds.end(); + ++iter) { + qWarning().nospace() << " " << iter.value() << "\t-> " << iter.key(); + } + + + QDeclarativeV4Compiler::dump(programData); + } + + return programData; +} + +void QDeclarativeV4Compiler::enableBindingsTest(bool e) +{ + if (e) + qmlBindingsTest = true; + else + qmlBindingsTest = qmlBindingsTestEnv(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecompiledbindings_p.h b/src/declarative/qml/v4/qdeclarativev4compiler_p.h index f3d8b4b804..c10691dc87 100644 --- a/src/declarative/qml/qdeclarativecompiledbindings_p.h +++ b/src/declarative/qml/v4/qdeclarativev4compiler_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QDECLARATIVEBINDINGOPTIMIZATIONS_P_H -#define QDECLARATIVEBINDINGOPTIMIZATIONS_P_H +#ifndef QDECLARATIVEV4COMPILER_P_H +#define QDECLARATIVEV4COMPILER_P_H // // W A R N I N G @@ -60,12 +60,14 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -struct QDeclarativeBindingCompilerPrivate; -class QDeclarativeBindingCompiler +class QDeclarativeFastProperties; +class QDeclarativeTypeNameCache; +class QDeclarativeV4CompilerPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeV4Compiler { public: - QDeclarativeBindingCompiler(); - ~QDeclarativeBindingCompiler(); + QDeclarativeV4Compiler(); + ~QDeclarativeV4Compiler(); // Returns true if bindings were compiled bool isValid() const; @@ -77,6 +79,7 @@ public: QDeclarativeParser::Property *property; QDeclarativeParser::Variant expression; QHash<QString, QDeclarativeParser::Object *> ids; + QDeclarativeTypeNameCache *importCache; QDeclarativeImports imports; }; @@ -87,30 +90,15 @@ public: QByteArray program() const; static void dump(const QByteArray &); + static QDeclarativeFastProperties *fastPropertyAccessor(); + static void enableBindingsTest(bool); private: - QDeclarativeBindingCompilerPrivate *d; -}; - -class QDeclarativeCompiledBindingsPrivate; -class QDeclarativeCompiledBindings : public QObject, public QDeclarativeAbstractExpression, public QDeclarativeRefCount -{ -public: - QDeclarativeCompiledBindings(const char *program, QDeclarativeContextData *context); - virtual ~QDeclarativeCompiledBindings(); - - QDeclarativeAbstractBinding *configBinding(int index, QObject *target, QObject *scope, int property); - -protected: - int qt_metacall(QMetaObject::Call, int, void **); - -private: - Q_DISABLE_COPY(QDeclarativeCompiledBindings) - Q_DECLARE_PRIVATE(QDeclarativeCompiledBindings) + QDeclarativeV4CompilerPrivate *d; }; QT_END_NAMESPACE QT_END_HEADER -#endif // QDECLARATIVEBINDINGOPTIMIZATIONS_P_H +#endif // QDECLARATIVEV4COMPILER_P_H diff --git a/src/declarative/qml/v4/qdeclarativev4compiler_p_p.h b/src/declarative/qml/v4/qdeclarativev4compiler_p_p.h new file mode 100644 index 0000000000..1b2a998422 --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4compiler_p_p.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEV4COMPILER_P_P_H +#define QDECLARATIVEV4COMPILER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qdeclarativev4instruction_p.h" +#include "qdeclarativev4ir_p.h" +#include <private/qdeclarativeparser_p.h> +#include <private/qdeclarativeimport_p.h> +#include <private/qdeclarativeengine_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeV4CompilerPrivate: protected QDeclarativeJS::IR::ExprVisitor, + protected QDeclarativeJS::IR::StmtVisitor +{ +public: + QDeclarativeV4CompilerPrivate(); + + void resetInstanceState(); + int commitCompile(); + + const QDeclarativeV4Compiler::Expression *expression; + QDeclarativeEnginePrivate *engine; + + QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((quintptr)expression->context, 16); } + + bool compile(QDeclarativeJS::AST::Node *); + + QHash<int, QPair<int, int> > registerCleanups; + + int registerLiteralString(quint8 reg, const QString &); + int registerString(const QString &); + QHash<QString, QPair<int, int> > registeredStrings; + QByteArray data; + + bool blockNeedsSubscription(const QStringList &); + int subscriptionIndex(const QStringList &); + quint32 subscriptionBlockMask(const QStringList &); + + quint8 exceptionId(quint32 line, quint32 column); + quint8 exceptionId(QDeclarativeJS::AST::ExpressionNode *); + QVector<quint64> exceptions; + + QHash<int, quint32> usedSubscriptionIds; + + QHash<QString, int> subscriptionIds; + QDeclarativeJS::Bytecode bytecode; + + // back patching + struct Patch { + QDeclarativeJS::IR::BasicBlock *block; // the basic block + int offset; // the index of the instruction to patch + Patch(QDeclarativeJS::IR::BasicBlock *block = 0, int index = -1) + : block(block), offset(index) {} + }; + QVector<Patch> patches; + + // Committed binding data + struct { + QList<int> offsets; + QList<QHash<int, quint32> > dependencies; + + //QDeclarativeJS::Bytecode bytecode; + QByteArray 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 convertToReal(QDeclarativeJS::IR::Expr *expr, int reg); + void convertToInt(QDeclarativeJS::IR::Expr *expr, int reg); + void convertToBool(QDeclarativeJS::IR::Expr *expr, int reg); + quint8 instructionOpcode(QDeclarativeJS::IR::Binop *e); + +protected: + + // + // tracing + // + void trace(int line, int column); + void trace(QVector<QDeclarativeJS::IR::BasicBlock *> *blocks); + void traceExpression(QDeclarativeJS::IR::Expr *e, quint8 r); + + inline void gen(const QDeclarativeJS::Instr &i) { bytecode.append(i); } + + // + // expressions + // + virtual void visitConst(QDeclarativeJS::IR::Const *e); + virtual void visitString(QDeclarativeJS::IR::String *e); + virtual void visitName(QDeclarativeJS::IR::Name *e); + virtual void visitTemp(QDeclarativeJS::IR::Temp *e); + virtual void visitUnop(QDeclarativeJS::IR::Unop *e); + virtual void visitBinop(QDeclarativeJS::IR::Binop *e); + virtual void visitCall(QDeclarativeJS::IR::Call *e); + + // + // statements + // + virtual void visitExp(QDeclarativeJS::IR::Exp *s); + virtual void visitMove(QDeclarativeJS::IR::Move *s); + virtual void visitJump(QDeclarativeJS::IR::Jump *s); + virtual void visitCJump(QDeclarativeJS::IR::CJump *s); + virtual void visitRet(QDeclarativeJS::IR::Ret *s); + +private: + QStringList _subscribeName; + QDeclarativeJS::IR::Function *_function; + QDeclarativeJS::IR::BasicBlock *_block; + void discard() { _discarded = true; } + bool _discarded; + quint8 currentReg; + + bool usedSubscriptionIdsChanged; + quint32 currentBlockMask; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEV4COMPILER_P_P_H + diff --git a/src/declarative/qml/v4/qdeclarativev4instruction.cpp b/src/declarative/qml/v4/qdeclarativev4instruction.cpp new file mode 100644 index 0000000000..1f24f30c25 --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4instruction.cpp @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativev4instruction_p.h" +#include "qdeclarativev4bindings_p.h" + +#include <QtCore/qdebug.h> +#include <private/qdeclarativeglobal_p.h> + +// Define this to do a test dump of all the instructions at startup. This is +// helpful to test that each instruction's Instr::dump() case uses the correct +// number of tabs etc and otherwise looks correct. +// #define DEBUG_INSTR_DUMP + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER) + +namespace QDeclarativeJS { + +#ifdef DEBUG_INSTR_DUMP +static struct DumpInstrAtStartup { + DumpInstrAtStartup() { +#define DUMP_INSTR_AT_STARTUP(Type, FMT) { Instr i; i.common.type = Instr::Type; i.dump(0); } + FOR_EACH_QML_INSTR(DUMP_INSTR_AT_STARTUP); + } +} dump_instr_at_startup; +#endif + +int Instr::size() const +{ +#define QML_RETURN_INSTR_SIZE(I, FMT) case I: return QML_INSTR_SIZE(I, FMT); + switch (common.type) { + FOR_EACH_QML_INSTR(QML_RETURN_INSTR_SIZE) + } +#undef QML_RETURN_INSTR_SIZE + return 0; +} + +void Instr::dump(int address) const +{ + QByteArray leading; + if (address != -1) { + leading = QByteArray::number(address); + leading.prepend(QByteArray(8 - leading.count(), ' ')); + leading.append("\t"); + } + +#define INSTR_DUMP qWarning().nospace() << leading.constData() + + switch (common.type) { + case Instr::Noop: + INSTR_DUMP << "\t" << "Noop"; + break; + case Instr::BindingId: + INSTR_DUMP << id.line << ":" << id.column << ":"; + break; + case Instr::Subscribe: + INSTR_DUMP << "\t" << "Subscribe" << "\t\t" << "Object_Reg(" << subscribeop.reg << ") Notify_Signal(" << subscribeop.index << ") -> Subscribe_Slot(" << subscribeop.offset << ")"; + break; + case Instr::SubscribeId: + INSTR_DUMP << "\t" << "SubscribeId" << "\t\t" << "Id_Offset(" << subscribeop.index << ") -> Subscribe_Slot(" << subscribeop.offset << ")"; + break; + case Instr::FetchAndSubscribe: + INSTR_DUMP << "\t" << "FetchAndSubscribe" << "\t" << "Object_Reg(" << fetchAndSubscribe.reg << ") Fast_Accessor(" << fetchAndSubscribe.function << ") -> Output_Reg(" << fetchAndSubscribe.reg << ") Subscription_Slot(" << fetchAndSubscribe.subscription << ")"; + break; + case Instr::LoadId: + INSTR_DUMP << "\t" << "LoadId" << "\t\t\t" << "Id_Offset(" << load.index << ") -> Output_Reg(" << load.reg << ")"; + break; + case Instr::LoadScope: + INSTR_DUMP << "\t" << "LoadScope" << "\t\t" << "-> Output_Reg(" << load.reg << ")"; + break; + case Instr::LoadRoot: + INSTR_DUMP << "\t" << "LoadRoot" << "\t\t" << "-> Output_Reg(" << load.reg << ")"; + break; + case Instr::LoadAttached: + INSTR_DUMP << "\t" << "LoadAttached" << "\t\t" << "Object_Reg(" << attached.reg << ") Attached_Index(" << attached.id << ") -> Output_Reg(" << attached.output << ")"; + break; + case Instr::UnaryNot: + INSTR_DUMP << "\t" << "UnaryNot" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::UnaryMinusReal: + INSTR_DUMP << "\t" << "UnaryMinusReal" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::UnaryMinusInt: + INSTR_DUMP << "\t" << "UnaryMinusInt" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::UnaryPlusReal: + INSTR_DUMP << "\t" << "UnaryPlusReal" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::UnaryPlusInt: + INSTR_DUMP << "\t" << "UnaryPlusInt" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertBoolToInt: + INSTR_DUMP << "\t" << "ConvertBoolToInt" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertBoolToReal: + INSTR_DUMP << "\t" << "ConvertBoolToReal" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertBoolToString: + INSTR_DUMP << "\t" << "ConvertBoolToString" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertIntToBool: + INSTR_DUMP << "\t" << "ConvertIntToBool" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertIntToReal: + INSTR_DUMP << "\t" << "ConvertIntToReal" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertIntToString: + INSTR_DUMP << "\t" << "ConvertIntToString" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertRealToBool: + INSTR_DUMP << "\t" << "ConvertRealToBool" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertRealToInt: + INSTR_DUMP << "\t" << "ConvertRealToInt" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertRealToString: + INSTR_DUMP << "\t" << "ConvertRealToString" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertStringToBool: + INSTR_DUMP << "\t" << "ConvertStringToBool" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertStringToInt: + INSTR_DUMP << "\t" << "ConvertStringToInt" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::ConvertStringToReal: + INSTR_DUMP << "\t" << "ConvertStringToReal" << "\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::MathSinReal: + INSTR_DUMP << "\t" << "MathSinReal" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::MathCosReal: + INSTR_DUMP << "\t" << "MathCosReal" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::MathRoundReal: + INSTR_DUMP << "\t" << "MathRoundReal" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::MathFloorReal: + INSTR_DUMP << "\t" << "MathFloorReal" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::MathPIReal: + INSTR_DUMP << "\t" << "MathPIReal" << "\t\t" << "Input_Reg(" << unaryop.src << ") -> Output_Reg(" << unaryop.output << ")"; + break; + case Instr::Real: + INSTR_DUMP << "\t" << "Real" << "\t\t\t" << "Constant(" << real_value.value << ") -> Output_Reg(" << real_value.reg << ")"; + break; + case Instr::Int: + INSTR_DUMP << "\t" << "Int" << "\t\t\t" << "Constant(" << int_value.value << ") -> Output_Reg(" << int_value.reg << ")"; + break; + case Instr::Bool: + INSTR_DUMP << "\t" << "Bool" << "\t\t\t" << "Constant(" << bool_value.value << ") -> Output_Reg(" << bool_value.reg << ")"; + break; + case Instr::String: + INSTR_DUMP << "\t" << "String" << "\t\t\t" << "String_DataIndex(" << string_value.offset << ") String_Length(" << string_value.length << ") -> Output_Register(" << string_value.reg << ")"; + break; + case Instr::EnableV4Test: + INSTR_DUMP << "\t" << "EnableV4Test" << "\t\t" << "String_DataIndex(" << string_value.offset << ") String_Length(" << string_value.length << ")"; + break; + case Instr::TestV4Store: + INSTR_DUMP << "\t" << "TestV4Store" << "\t\t" << "Input_Reg(" << storetest.reg << ") Reg_Type(" << storetest.regType << ")"; + break; + case Instr::BitAndInt: + INSTR_DUMP << "\t" << "BitAndInt" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::BitOrInt: + INSTR_DUMP << "\t" << "BitOrInt" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::BitXorInt: + INSTR_DUMP << "\t" << "BitXorInt" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::AddReal: + INSTR_DUMP << "\t" << "AddReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::AddString: + INSTR_DUMP << "\t" << "AddString" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::SubReal: + INSTR_DUMP << "\t" << "SubReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::MulReal: + INSTR_DUMP << "\t" << "MulReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::DivReal: + INSTR_DUMP << "\t" << "DivReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::ModReal: + INSTR_DUMP << "\t" << "ModReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::LShiftInt: + INSTR_DUMP << "\t" << "LShiftInt" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::RShiftInt: + INSTR_DUMP << "\t" << "RShiftInt" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::URShiftInt: + INSTR_DUMP << "\t" << "URShiftInt" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::GtReal: + INSTR_DUMP << "\t" << "GtReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::LtReal: + INSTR_DUMP << "\t" << "LtReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::GeReal: + INSTR_DUMP << "\t" << "GeReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::LeReal: + INSTR_DUMP << "\t" << "LeReal" << "\t\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::EqualReal: + INSTR_DUMP << "\t" << "EqualReal" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::NotEqualReal: + INSTR_DUMP << "\t" << "NotEqualReal" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::StrictEqualReal: + INSTR_DUMP << "\t" << "StrictEqualReal" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::StrictNotEqualReal: + INSTR_DUMP << "\t" << "StrictNotEqualReal" << "\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::GtString: + INSTR_DUMP << "\t" << "GtString" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::LtString: + INSTR_DUMP << "\t" << "LtString" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::GeString: + INSTR_DUMP << "\t" << "GeString" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::LeString: + INSTR_DUMP << "\t" << "LeString" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::EqualString: + INSTR_DUMP << "\t" << "EqualString" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::NotEqualString: + INSTR_DUMP << "\t" << "NotEqualString" << "\t\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::StrictEqualString: + INSTR_DUMP << "\t" << "StrictEqualString" << "\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::StrictNotEqualString: + INSTR_DUMP << "\t" << "StrictNotEqualString" << "\t" << "Input_Reg(" << binaryop.left << ") Input_Reg(" << binaryop.right << ") -> Output_Reg(" << binaryop.output << ")"; + break; + case Instr::NewString: + INSTR_DUMP << "\t" << "NewString" << "\t\t" << "Register(" << construct.reg << ")"; + break; + case Instr::NewUrl: + INSTR_DUMP << "\t" << "NewUrl" << "\t\t\t" << "Register(" << construct.reg << ")"; + break; + case Instr::CleanupRegister: + INSTR_DUMP << "\t" << "CleanupRegister" << "\t\t" << "Register(" << cleanup.reg << ")"; + break; + case Instr::Fetch: + INSTR_DUMP << "\t" << "Fetch" << "\t\t\t" << "Object_Reg(" << fetch.reg << ") Property_Index(" << fetch.index << ") -> Output_Reg(" << fetch.reg << ")"; + break; + case Instr::Store: + INSTR_DUMP << "\t" << "Store" << "\t\t\t" << "Input_Reg(" << store.reg << ") -> Object_Reg(" << store.output << ") Property_Index(" << store.index << ")"; + break; + case Instr::Copy: + INSTR_DUMP << "\t" << "Copy" << "\t\t\t" << "Input_Reg(" << copy.src << ") -> Output_Reg(" << copy.reg << ")"; + break; + case Instr::Jump: + if (jump.reg != -1) { + INSTR_DUMP << "\t" << "Jump" << "\t\t\t" << "Address(" << (address + size() + jump.count) << ") [if false == Input_Reg(" << jump.reg << ")]"; + } else { + INSTR_DUMP << "\t" << "Jump" << "\t\t\t" << "Address(" << (address + size() + jump.count) << ")"; + } + break; + case Instr::BranchFalse: + INSTR_DUMP << "\t" << "BranchFalse" << "\t\t" << "Address(" << (address + size() + branchop.offset) << ") [if false == Input_Reg(" << branchop.reg << ")]"; + break; + case Instr::BranchTrue: + INSTR_DUMP << "\t" << "BranchTrue" << "\t\t" << "Address(" << (address + size() + branchop.offset) << ") [if true == Input_Reg(" << branchop.reg << ")]"; + break; + case Instr::Branch: + INSTR_DUMP << "\t" << "Branch" << "\t\t\t" << "Address(" << (address + size() + branchop.offset) << ")"; + break; + case Instr::InitString: + INSTR_DUMP << "\t" << "InitString" << "\t\t" << "String_DataIndex(" << initstring.dataIdx << ") -> String_Slot(" << initstring.offset << ")"; + break; + case Instr::Block: + INSTR_DUMP << "\t" << "Block" << "\t\t\t" << "Mask(" << QByteArray::number(blockop.block, 16).constData() << ")"; + break; + default: + INSTR_DUMP << "\t" << "Unknown"; + break; + } +} + +void Instr::noop() +{ + common.type = Noop; +} + +void Instr::load_root(quint8 reg) +{ + common.type = LoadRoot; + load.reg = reg; + load.index = 0; +} + +void Instr::load_scope(quint8 reg) +{ + common.type = LoadScope; + load.reg = reg; + load.index = 0; +} + +void Instr::load_id(quint8 reg, quint32 idIndex) +{ + common.type = LoadId; + load.reg = reg; + load.index = idIndex; +} + +void Instr::subscribe(qint8 reg, quint16 subscribeSlot, quint32 notifyIndex) +{ + common.type = Instr::Subscribe; + subscribeop.reg = reg; + subscribeop.offset = subscribeSlot; + subscribeop.index = notifyIndex; +} + +void Instr::subscribeId(qint8 reg, quint16 subscribeSlot, quint32 idIndex) +{ + common.type = Instr::SubscribeId; + subscribeop.reg = reg; + subscribeop.offset = subscribeSlot; + subscribeop.index = idIndex; +} + +void Instr::move_reg_bool(quint8 reg, bool value) +{ + common.type = Bool; + bool_value.reg = reg; + bool_value.value = value; +} + +void Instr::move_reg_int(quint8 reg, int value) +{ + common.type = Int; + int_value.reg = reg; + int_value.value = value; +} + +void Instr::move_reg_qreal(quint8 reg, qreal value) +{ + common.type = Real; + real_value.reg = reg; + real_value.value = value; +} + +void Instr::move_reg_reg(quint8 reg, quint8 src) +{ + common.type = Copy; + copy.reg = reg; + copy.src = src; +} + +void Instr::unary_not(quint8 dest, quint8 src) +{ + common.type = UnaryNot; + unaryop.src = src; + unaryop.output = dest; +} + +void Instr::uminus_real(quint8 dest, quint8 src) +{ + common.type = UnaryMinusReal; + unaryop.src = src; + unaryop.output = dest; +} + +void Instr::uminus_int(quint8 dest, quint8 src) +{ + common.type = UnaryMinusInt; + unaryop.src = src; + unaryop.output = dest; +} + +void Instr::uplus_real(quint8 dest, quint8 src) +{ + common.type = UnaryPlusReal; + unaryop.src = src; + unaryop.output = dest; +} + +void Instr::uplus_int(quint8 dest, quint8 src) +{ + common.type = UnaryPlusInt; + unaryop.src = src; + unaryop.output = dest; +} + +void Instr::ucompl_real(quint8 dest, quint8 src) +{ + Q_UNUSED(dest); + Q_UNUSED(src); + if (qmlVerboseCompiler()) + qWarning() << "TODO" << Q_FUNC_INFO; +} + +void Instr::ucompl_int(quint8 dest, quint8 src) +{ + Q_UNUSED(dest); + Q_UNUSED(src); + if (qmlVerboseCompiler()) + qWarning() << "TODO" << Q_FUNC_INFO; +} + +void Instr::math_sin_real(quint8 reg) +{ + common.type = MathSinReal; + unaryop.src = reg; + unaryop.output = reg; +} + +void Instr::math_cos_real(quint8 reg) +{ + common.type = MathCosReal; + unaryop.src = reg; + unaryop.output = reg; +} + +void Instr::math_round_real(quint8 reg) +{ + common.type = MathRoundReal; + unaryop.src = reg; + unaryop.output = reg; +} + +void Instr::math_floor_real(quint8 reg) +{ + common.type = MathFloorReal; + unaryop.src = reg; + unaryop.output = reg; +} + +void Instr::math_pi_real(quint8 reg) +{ + common.type = MathPIReal; + unaryop.src = reg; + unaryop.output = reg; +} + +void Instr::branch_true(quint8 reg, qint16 offset) +{ + common.type = BranchTrue; + branchop.reg = reg; + branchop.offset = offset; +} + +void Instr::branch_false(quint8 reg, qint16 offset) +{ + common.type = BranchFalse; + branchop.reg = reg; + branchop.offset = offset; +} + +void Instr::branch(qint16 offset) +{ + common.type = Branch; + branchop.offset = offset; +} + +void Instr::block(quint32 mask) +{ + common.type = Block; + blockop.block = mask; +} + +Bytecode::Bytecode() +{ +#ifdef QML_THREADED_INTERPRETER + decodeInstr = QDeclarativeV4Bindings::getDecodeInstrTable(); +#endif +} + +void Bytecode::append(const Instr &instr) +{ + const char *it; +#ifdef QML_THREADED_INTERPRETER + Instr i = instr; + i.common.code = decodeInstr[i.common.type]; + it = (const char *) &i; +#else + it = (const char *) &instr; +#endif + d.append(it, instr.size()); +} + +void Bytecode::append(const QVector<Instr> &instrs) +{ + foreach (const Instr &i, instrs) + append(i); +} + +int Bytecode::remove(int offset) +{ + const Instr *instr = (const Instr *) (d.begin() + offset); + const int instrSize = instr->size(); + d.remove(offset, instrSize); + return instrSize; +} + +const Instr &Bytecode::operator[](int offset) const +{ + return *((const Instr *) (d.begin() + offset)); +} + +Instr &Bytecode::operator[](int offset) +{ + return *((Instr *) (d.begin() + offset)); +} + +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v4/qdeclarativev4instruction_p.h b/src/declarative/qml/v4/qdeclarativev4instruction_p.h new file mode 100644 index 0000000000..f6e0bc734a --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4instruction_p.h @@ -0,0 +1,444 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEV4INSTRUCTION_P_H +#define QDECLARATIVEV4INSTRUCTION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qvector.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#define FOR_EACH_QML_INSTR(F) \ + F(Noop, common) \ + F(BindingId, id) \ + F(Subscribe, subscribeop) \ + F(SubscribeId, subscribeop) \ + F(FetchAndSubscribe, fetchAndSubscribe) \ + F(LoadId, load) \ + F(LoadScope, load) \ + F(LoadRoot, load) \ + F(LoadAttached, attached) \ + F(UnaryNot, unaryop) \ + F(UnaryMinusReal, unaryop) \ + F(UnaryMinusInt, unaryop) \ + F(UnaryPlusReal, unaryop) \ + F(UnaryPlusInt, unaryop) \ + F(ConvertBoolToInt, unaryop) \ + F(ConvertBoolToReal, unaryop) \ + F(ConvertBoolToString, unaryop) \ + F(ConvertIntToBool, unaryop) \ + F(ConvertIntToReal, unaryop) \ + F(ConvertIntToString, unaryop) \ + F(ConvertRealToBool, unaryop) \ + F(ConvertRealToInt, unaryop) \ + F(ConvertRealToString, unaryop) \ + F(ConvertStringToBool, unaryop) \ + F(ConvertStringToInt, unaryop) \ + F(ConvertStringToReal, unaryop) \ + F(MathSinReal, unaryop) \ + F(MathCosReal, unaryop) \ + F(MathRoundReal, unaryop) \ + F(MathFloorReal, unaryop) \ + F(MathPIReal, unaryop) \ + F(Real, real_value) \ + F(Int, int_value) \ + F(Bool, bool_value) \ + F(String, string_value) \ + F(EnableV4Test, string_value) \ + F(TestV4Store, storetest) \ + F(BitAndInt, binaryop) \ + F(BitOrInt, binaryop) \ + F(BitXorInt, binaryop) \ + F(AddReal, binaryop) \ + F(AddString, binaryop) \ + F(SubReal, binaryop) \ + F(MulReal, binaryop) \ + F(DivReal, binaryop) \ + F(ModReal, binaryop) \ + F(LShiftInt, binaryop) \ + F(RShiftInt, binaryop) \ + F(URShiftInt, binaryop) \ + F(GtReal, binaryop) \ + F(LtReal, binaryop) \ + F(GeReal, binaryop) \ + F(LeReal, binaryop) \ + F(EqualReal, binaryop) \ + F(NotEqualReal, binaryop) \ + F(StrictEqualReal, binaryop) \ + F(StrictNotEqualReal, binaryop) \ + F(GtString, binaryop) \ + F(LtString, binaryop) \ + F(GeString, binaryop) \ + F(LeString, binaryop) \ + F(EqualString, binaryop) \ + F(NotEqualString, binaryop) \ + F(StrictEqualString, binaryop) \ + F(StrictNotEqualString, binaryop) \ + F(NewString, construct) \ + F(NewUrl, construct) \ + F(CleanupRegister, cleanup) \ + F(Copy, copy) \ + F(Fetch, fetch) \ + F(Store, store) \ + F(Jump, jump) \ + F(BranchTrue, branchop) \ + F(BranchFalse, branchop) \ + F(Branch, branchop) \ + F(Block, blockop) \ + /* Speculative property resolution */ \ + F(InitString, initstring) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define QML_THREADED_INTERPRETER +#endif + +#ifdef Q_ALIGNOF +# define QML_INSTR_ALIGN_MASK (Q_ALIGNOF(Instr) - 1) +#else +# define QML_INSTR_ALIGN_MASK (sizeof(void *) - 1) +#endif + +#define QML_INSTR_ENUM(I, FMT) I, +#define QML_INSTR_ADDR(I, FMT) &&op_##I, +#define QML_INSTR_SIZE(I, FMT) ((sizeof(Instr::instr_##FMT) + QML_INSTR_ALIGN_MASK) & ~QML_INSTR_ALIGN_MASK) + +#ifdef QML_THREADED_INTERPRETER +# define QML_BEGIN_INSTR(I,FMT) op_##I: +# define QML_END_INSTR(I,FMT) code += QML_INSTR_SIZE(I, FMT); instr = (const Instr *) code; goto *instr->common.code; +# define QML_INSTR_HEADER void *code; +#else +# define QML_BEGIN_INSTR(I,FMT) case Instr::I: +# define QML_END_INSTR(I,FMT) code += QML_INSTR_SIZE(I, FMT); instr = (const Instr *) code; break; +# define QML_INSTR_HEADER +#endif + +namespace QDeclarativeJS { + +union Instr { + int size() const; + void dump(int = -1) const; + void noop(); + void load_root(quint8 reg); + void load_scope(quint8 reg); + void load_id(quint8 reg, quint32 idIndex); + void subscribe(qint8 reg, quint16 offset, quint32 index); + void subscribeId(qint8 reg, quint16 offset, quint32 index); + void move_reg_bool(quint8 reg, bool value); + void move_reg_int(quint8 reg, int value); + void move_reg_qreal(quint8 reg, qreal value); + void move_reg_reg(quint8 reg, quint8 src); + + void unary_not(quint8 dest, quint8 src); + void uminus_real(quint8 dest, quint8 src); + void uminus_int(quint8 dest, quint8 src); + void uplus_real(quint8 dest, quint8 src); + void uplus_int(quint8 dest, quint8 src); + void ucompl_real(quint8 dest, quint8 src); + void ucompl_int(quint8 dest, quint8 src); + + void math_sin_real(quint8 reg); + void math_cos_real(quint8 reg); + void math_round_real(quint8 reg); + void math_floor_real(quint8 reg); + void math_pi_real(quint8 reg); + void branch_true(quint8 reg, qint16 offset); + void branch_false(quint8 reg, qint16 offset); + void branch(qint16 offset); + void block(quint32 mask); + + enum { + FOR_EACH_QML_INSTR(QML_INSTR_ENUM) + }; + + struct instr_common { + QML_INSTR_HEADER + quint8 type; + }; + + struct instr_id { + QML_INSTR_HEADER + quint8 type; + quint16 column; + quint32 line; + }; + + struct instr_init { + QML_INSTR_HEADER + quint8 type; + quint16 subscriptions; + quint16 identifiers; + }; + + struct instr_subscribeop { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint16 offset; + quint32 index; + }; + + struct instr_load { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint32 index; + }; + + struct instr_attached { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 id; + }; + + struct instr_store { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 index; + }; + + struct instr_storetest { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + qint32 regType; + }; + + struct instr_fetchAndSubscribe { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 exceptionId; + quint8 valueType; + quint16 subscription; + quint16 function; + }; + + struct instr_fetch{ + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 exceptionId; + quint8 valueType; + quint32 index; + }; + + struct instr_copy { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + qint8 src; + }; + + struct instr_construct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + }; + + struct instr_real_value { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + qreal value; // XXX Makes the instruction 12 bytes + }; + + struct instr_int_value { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + int value; + }; + + struct instr_bool_value { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + bool value; + }; + + struct instr_string_value { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint16 length; + quint32 offset; + }; + + struct instr_binaryop { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 left; + qint8 right; + }; + + struct instr_unaryop { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 src; + }; + + struct instr_jump { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint32 count; + }; + + struct instr_find { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + qint8 src; + quint8 exceptionId; + quint16 name; + quint16 subscribeIndex; + }; + + struct instr_cleanup { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + }; + + struct instr_initstring { + QML_INSTR_HEADER + quint8 type; + quint16 offset; + quint32 dataIdx; + }; + + struct instr_branchop { + QML_INSTR_HEADER + quint8 type; + quint8 reg; + qint16 offset; + }; + + struct instr_blockop { + QML_INSTR_HEADER + quint8 type; + quint32 block; + }; + + instr_common common; + instr_id id; + instr_init init; + instr_subscribeop subscribeop; + instr_load load; + instr_attached attached; + instr_store store; + instr_storetest storetest; + instr_fetchAndSubscribe fetchAndSubscribe; + instr_fetch fetch; + instr_copy copy; + instr_construct construct; + instr_real_value real_value; + instr_int_value int_value; + instr_bool_value bool_value; + instr_string_value string_value; + instr_binaryop binaryop; + instr_unaryop unaryop; + instr_jump jump; + instr_find find; + instr_cleanup cleanup; + instr_initstring initstring; + instr_branchop branchop; + instr_blockop blockop; +}; + +class Bytecode +{ + Q_DISABLE_COPY(Bytecode) + +public: + Bytecode(); + + QByteArray code() const { return d; } + const char *constData() const { return d.constData(); } + int size() const { return d.size(); } + int count() const { return d.count(); } + void clear() { d.clear(); } + bool isEmpty() const { return d.isEmpty(); } + void append(const Instr &instr); + void append(const QVector<Instr> &instrs); + int remove(int index); + + const Instr &operator[](int offset) const; + Instr &operator[](int offset); + +private: + QByteArray d; +#ifdef QML_THREADED_INTERPRETER + void **decodeInstr; +#endif +}; + +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEV4INSTRUCTION_P_H + diff --git a/src/declarative/qml/v4/qdeclarativev4ir.cpp b/src/declarative/qml/v4/qdeclarativev4ir.cpp new file mode 100644 index 0000000000..7876e6ccea --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4ir.cpp @@ -0,0 +1,832 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativev4ir_p.h" + +#include <QtCore/qtextstream.h> +#include <QtCore/qdebug.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + +namespace QDeclarativeJS { +namespace IR { + +inline const char *typeName(Type t) +{ + switch (t) { + case InvalidType: return "invalid"; + case UndefinedType: return "undefined"; + case NullType: return "null"; + case VoidType: return "void"; + case StringType: return "string"; + case UrlType: return "url"; + case AnchorLineType: return "AnchorLine"; + case SGAnchorLineType: return "SGAnchorLine"; + case AttachType: return "AttachType"; + case ObjectType: return "object"; + case BoolType: return "bool"; + case IntType: return "int"; + case RealType: return "qreal"; + case RealNaNType: return "NaN"; + default: return "invalid"; + } +} + +IR::Type maxType(IR::Type left, IR::Type right) +{ + if (left == right) + return left; + else if (left >= IR::FirstNumberType && right >= IR::FirstNumberType) + return qMax(left, right); + else if ((left >= IR::FirstNumberType && right == IR::StringType) || + (right >= IR::FirstNumberType && left == IR::StringType)) + return IR::StringType; + else + return IR::InvalidType; +} + + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + default: return "?"; + + } // switch +} + +AluOp binaryOperator(int op) +{ + switch (static_cast<QSOperator::Op>(op)) { + case QSOperator::Add: return OpAdd; + case QSOperator::And: return OpAnd; + case QSOperator::BitAnd: return OpBitAnd; + case QSOperator::BitOr: return OpBitOr; + case QSOperator::BitXor: return OpBitXor; + case QSOperator::Div: return OpDiv; + case QSOperator::Equal: return OpEqual; + case QSOperator::Ge: return OpGe; + case QSOperator::Gt: return OpGt; + case QSOperator::Le: return OpLe; + case QSOperator::LShift: return OpLShift; + case QSOperator::Lt: return OpLt; + case QSOperator::Mod: return OpMod; + case QSOperator::Mul: return OpMul; + case QSOperator::NotEqual: return OpNotEqual; + case QSOperator::Or: return OpOr; + case QSOperator::RShift: return OpRShift; + case QSOperator::StrictEqual: return OpStrictEqual; + case QSOperator::StrictNotEqual: return OpStrictNotEqual; + case QSOperator::Sub: return OpSub; + case QSOperator::URShift: return OpURShift; + default: return OpInvalid; + } +} + +void Const::dump(QTextStream &out) +{ + out << value; +} + +void String::dump(QTextStream &out) +{ + out << '"' << escape(value) << '"'; +} + +QString String::escape(const QString &s) +{ + QString r; + foreach (const QChar &ch, s) { + if (ch == QLatin1Char('\n')) + r += QLatin1String("\\n"); + else if (ch == QLatin1Char('\r')) + r += QLatin1String("\\r"); + else if (ch == QLatin1Char('\\')) + r += QLatin1String("\\\\"); + else if (ch == QLatin1Char('"')) + r += QLatin1String("\\\""); + else if (ch == QLatin1Char('\'')) + r += QLatin1String("\\'"); + else + r += ch; + } + return r; +} + +Name::Name(Name *base, Type type, const QString &id, Symbol symbol, quint32 line, quint32 column) +: Expr(type) + , base(base) + , id(id) + , symbol(symbol) + , ptr(0) + , index(-1) + , storage(MemberStorage) + , builtin(NoBuiltinSymbol) + , line(line) + , column(column) +{ + if (id.length() == 8 && id == QLatin1String("Math.sin")) { + builtin = MathSinBultinFunction; + } else if (id.length() == 8 && id == QLatin1String("Math.cos")) { + builtin = MathCosBultinFunction; + } else if (id.length() == 10 && id == QLatin1String("Math.round")) { + builtin = MathRoundBultinFunction; + } else if (id.length() == 10 && id == QLatin1String("Math.floor)")) { + builtin = MathFloorBultinFunction; + } else if (id.length() == 7 && id == QLatin1String("Math.PI")) { + builtin = MathPIBuiltinConstant; + type = RealType; + } +} + +void Name::dump(QTextStream &out) +{ + if (base) { + base->dump(out); + out << '.'; + } + + out << id; +} + +void Temp::dump(QTextStream &out) +{ + out << 't' << index; +} + +void Unop::dump(QTextStream &out) +{ + out << opname(op); + expr->dump(out); +} + +Type Unop::typeForOp(AluOp op, Expr *expr) +{ + switch (op) { + case OpIfTrue: return BoolType; + case OpNot: return BoolType; + + case OpUMinus: + case OpUPlus: + case OpCompl: + return maxType(expr->type, RealType); + + default: + break; + } + + return InvalidType; +} + +void Binop::dump(QTextStream &out) +{ + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); +} + +Type Binop::typeForOp(AluOp op, Expr *left, Expr *right) +{ + if (! (left && right)) + return InvalidType; + + switch (op) { + case OpInvalid: + return InvalidType; + + // unary operators + case OpIfTrue: + case OpNot: + case OpUMinus: + case OpUPlus: + case OpCompl: + return InvalidType; + + // bit fields + case OpBitAnd: + case OpBitOr: + case OpBitXor: + return IntType; + + case OpAdd: + if (left->type == StringType) + return StringType; + return RealType; + + case OpSub: + case OpMul: + case OpDiv: + case OpMod: + return RealType; + + case OpLShift: + case OpRShift: + case OpURShift: + return IntType; + + case OpAnd: + case OpOr: + return BoolType; + + case OpGt: + case OpLt: + case OpGe: + case OpLe: + case OpEqual: + case OpNotEqual: + case OpStrictEqual: + case OpStrictNotEqual: + return BoolType; + } // switch + + return InvalidType; +} + +void Call::dump(QTextStream &out) +{ + base->dump(out); + out << '('; + for (int i = 0; i < args.size(); ++i) { + if (i) + out << ", "; + args.at(i)->dump(out); + } + out << ')'; +} + +Type Call::typeForFunction(Expr *base) +{ + if (! base) + return InvalidType; + + if (Name *name = base->asName()) { + switch (name->builtin) { + case MathSinBultinFunction: + case MathCosBultinFunction: + return RealType; + + case MathRoundBultinFunction: + case MathFloorBultinFunction: + return IntType; + + case NoBuiltinSymbol: + case MathPIBuiltinConstant: + break; + } + } // switch + + return InvalidType; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << " = "; + if (source->type != target->type) + out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); + if (source->type != target->type) + out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';'; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +Function::~Function() +{ + qDeleteAll(basicBlocks); + qDeleteAll(temps); +} + +BasicBlock *Function::newBasicBlock() +{ + const int index = basicBlocks.size(); + return i(new BasicBlock(this, index)); +} + +void Function::dump(QTextStream &out) +{ + QString fname; + if (name) + fname = name->asString(); + else + fname = QLatin1String("$anonymous"); + out << "function " << fname << "() {" << endl; + foreach (BasicBlock *bb, basicBlocks) { + bb->dump(out); + } + out << '}' << endl; +} + +Temp *BasicBlock::TEMP(Type type, int index) +{ + return function->e(new Temp(type, index)); +} + +Temp *BasicBlock::TEMP(Type type) +{ + return TEMP(type, function->tempCount++); +} + +Expr *BasicBlock::CONST(double value) +{ + return CONST(IR::RealType, value); +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + return function->e(new Const(type, value)); +} + +Expr *BasicBlock::STRING(const QString &value) +{ + return function->e(new String(value)); +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + return NAME(0, id, line, column); +} + +Name *BasicBlock::NAME(Name *base, const QString &id, quint32 line, quint32 column) +{ + return function->e(new Name(base, InvalidType, id, Name::Unbound, line, column)); +} + +Name *BasicBlock::SYMBOL(Type type, const QString &id, const QMetaObject *meta, int index, Name::Storage storage, + quint32 line, quint32 column) +{ + Name *name = SYMBOL(/*base = */ 0, type, id, meta, index, line, column); + name->storage = storage; + return name; +} + +Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, int index, Name::Storage storage, + quint32 line, quint32 column) +{ + Name *name = new Name(base, type, id, Name::Property, line, column); + name->meta = meta; + name->index = index; + name->storage = storage; + return function->e(name); +} + +Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, int index, + quint32 line, quint32 column) +{ + Name *name = new Name(base, type, id, Name::Property, line, column); + name->meta = meta; + name->index = index; + return function->e(name); +} + +Name *BasicBlock::ID_OBJECT(const QString &id, const QDeclarativeParser::Object *object, quint32 line, quint32 column) +{ + Name *name = new Name(/*base = */ 0, IR::ObjectType, id, Name::IdObject, line, column); + name->idObject = object; + name->index = object->idIndex; + name->storage = Name::IdStorage; + return function->e(name); +} + +Name *BasicBlock::ATTACH_TYPE(const QString &id, const QDeclarativeType *attachType, Name::Storage storage, + quint32 line, quint32 column) +{ + Name *name = new Name(/*base = */ 0, IR::AttachType, id, Name::AttachType, line, column); + name->declarativeType = attachType; + name->storage = storage; + return function->e(name); +} + + +Expr *BasicBlock::UNOP(AluOp op, Expr *expr) +{ + return function->e(new Unop(op, expr)); +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + if (left && right) { + if (Const *c1 = left->asConst()) { + if (Const *c2 = right->asConst()) { + switch (op) { + case OpAdd: return CONST(c1->value + c2->value); + case OpAnd: return CONST(c1->value ? c2->value : 0); + case OpBitAnd: return CONST(int(c1->value) & int(c2->value)); + case OpBitOr: return CONST(int(c1->value) | int(c2->value)); + case OpBitXor: return CONST(int(c1->value) ^ int(c2->value)); + case OpDiv: return CONST(c1->value / c2->value); + case OpEqual: return CONST(c1->value == c2->value); + case OpGe: return CONST(c1->value >= c2->value); + case OpGt: return CONST(c1->value > c2->value); + case OpLe: return CONST(c1->value <= c2->value); + case OpLShift: return CONST(int(c1->value) << int(c2->value)); + case OpLt: return CONST(c1->value < c2->value); + case OpMod: return CONST(::fmod(c1->value, c2->value)); + case OpMul: return CONST(c1->value * c2->value); + case OpNotEqual: return CONST(c1->value != c2->value); + case OpOr: return CONST(c1->value ? c1->value : c2->value); + case OpRShift: return CONST(int(c1->value) >> int(c2->value)); + case OpStrictEqual: return CONST(c1->value == c2->value); + case OpStrictNotEqual: return CONST(c1->value != c2->value); + case OpSub: return CONST(c1->value - c2->value); + case OpURShift: return CONST(unsigned(c1->value) >> int(c2->value)); + + case OpIfTrue: // unary ops + case OpNot: + case OpUMinus: + case OpUPlus: + case OpCompl: + case OpInvalid: + break; + } + } + } + } + + return function->e(new Binop(op, left, right)); +} + +Expr *BasicBlock::CALL(Expr *base, const QVector<Expr *> &args) +{ + return function->e(new Call(base, args)); +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + return i(new Exp(expr)); +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, bool isMoveForReturn) +{ + return i(new Move(target, source, isMoveForReturn)); +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + else + return i(new Jump(target)); +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + return i(new CJump(cond, iftrue, iffalse)); +} + +Stmt *BasicBlock::RET(Expr *expr, Type type, quint32 line, quint32 column) +{ + if (isTerminated()) + return 0; + else + return i(new Ret(expr, type, line, column)); +} + +void BasicBlock::dump(QTextStream &out) +{ + out << 'L' << this << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out); + out << endl; + } +} + +void Module::dump(QTextStream &out) +{ + foreach (Function *fun, functions) { + fun->dump(out); + out << endl; + } +} + +#ifdef DEBUG_IR_STRUCTURE + +static const char *symbolname(Name::Symbol s) +{ + switch (s) { + case Name::Unbound: + return "Unbound"; + case Name::IdObject: + return "IdObject"; + case Name::AttachType: + return "AttachType"; + case Name::Object: + return "Object"; + case Name::Property: + return "Property"; + case Name::Slot: + return "Slot"; + default: + Q_ASSERT(!"Unreachable"); + return "Unknown"; + } +} + +static const char *storagename(Name::Storage s) +{ + switch (s) { + case Name::MemberStorage: + return "MemberStorage"; + case Name::IdStorage: + return "IdStorage"; + case Name::RootStorage: + return "RootStorage"; + case Name::ScopeStorage: + return "ScopeStorage"; + default: + Q_ASSERT(!"Unreachable"); + return "UnknownStorage"; + } +} + +IRDump::IRDump() +: indentSize(0) +{ +} + +void IRDump::inc() +{ + indentSize++; + indentData = QByteArray(indentSize * 4, ' '); +} + +void IRDump::dec() +{ + indentSize--; + indentData = QByteArray(indentSize * 4, ' '); +} + +void IRDump::dec(); + +void IRDump::expression(QDeclarativeJS::IR::Expr *e) +{ + inc(); + + e->accept(this); + + dec(); +} + +void IRDump::basicblock(QDeclarativeJS::IR::BasicBlock *b) +{ + inc(); + + qWarning().nospace() << indent() << "BasicBlock " << b << " {"; + for (int ii = 0; ii < b->statements.count(); ++ii) { + statement(b->statements.at(ii)); + if (ii != (b->statements.count() - 1)) + qWarning(); + } + qWarning().nospace() << indent() << "}"; + + dec(); +} + +void IRDump::statement(QDeclarativeJS::IR::Stmt *s) +{ + inc(); + + s->accept(this); + + dec(); +} + +void IRDump::function(QDeclarativeJS::IR::Function *f) +{ + inc(); + + qWarning().nospace() << indent() << "Function {"; + for (int ii = 0; ii < f->basicBlocks.count(); ++ii) { + basicblock(f->basicBlocks.at(ii)); + } + qWarning().nospace() << indent() << "}"; + + dec(); +} + +const char *IRDump::indent() +{ + return indentData.constData(); +} + +void IRDump::visitConst(QDeclarativeJS::IR::Const *e) +{ + qWarning().nospace() << indent() << "Const:Expr { type: " << typeName(e->type) << ", value: " << e->value << "}"; +} + +void IRDump::visitString(QDeclarativeJS::IR::String *e) +{ + qWarning().nospace() << indent() << "String:Expr { type: " << typeName(e->type) << ", value: " << e->value << "}"; +} + +static void namedumprecur(QDeclarativeJS::IR::Name *e, const char *indent) +{ + if (e->base) namedumprecur(e->base, indent); + qWarning().nospace() << indent << " { type: " << typeName(e->type) << ", symbol: " << symbolname(e->symbol) << ", storage: " << storagename(e->storage) << ", id: " << e->id << "}"; +} + +void IRDump::visitName(QDeclarativeJS::IR::Name *e) +{ + qWarning().nospace() << indent() << "Name:Expr {"; + namedumprecur(e, indent()); + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitTemp(QDeclarativeJS::IR::Temp *e) +{ + qWarning().nospace() << indent() << "Temp:Expr { type: " << typeName(e->type) << ", index: " << e->index << " }"; +} + +void IRDump::visitUnop(QDeclarativeJS::IR::Unop *e) +{ + qWarning().nospace() << indent() << "Unop:Expr { "; + qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op); + qWarning().nospace() << indent() << " expr: {"; + expression(e->expr); + qWarning().nospace() << indent() << " }"; + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitBinop(QDeclarativeJS::IR::Binop *e) +{ + qWarning().nospace() << indent() << "Binop:Expr { "; + qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op); + qWarning().nospace() << indent() << " left: {"; + inc(); + expression(e->left); + dec(); + qWarning().nospace() << indent() << " },"; + qWarning().nospace() << indent() << " right: {"; + inc(); + expression(e->right); + dec(); + qWarning().nospace() << indent() << " }"; + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitCall(QDeclarativeJS::IR::Call *e) +{ + Q_UNUSED(e); + qWarning().nospace() << indent() << "Exp::Call { }"; +} + +void IRDump::visitExp(QDeclarativeJS::IR::Exp *s) +{ + qWarning().nospace() << indent() << "Exp:Stmt {"; + expression(s->expr); + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitMove(QDeclarativeJS::IR::Move *s) +{ + qWarning().nospace() << indent() << "Move:Stmt {"; + qWarning().nospace() << indent() << " isMoveForReturn: " << s->isMoveForReturn; + qWarning().nospace() << indent() << " target: {"; + inc(); + expression(s->target); + dec(); + qWarning().nospace() << indent() << " },"; + qWarning().nospace() << indent() << " source: {"; + inc(); + expression(s->source); + dec(); + qWarning().nospace() << indent() << " }"; + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitJump(QDeclarativeJS::IR::Jump *s) +{ + qWarning().nospace() << indent() << "Jump:Stmt { BasicBlock(" << s->target << ") }"; +} + +void IRDump::visitCJump(QDeclarativeJS::IR::CJump *s) +{ + qWarning().nospace() << indent() << "CJump:Stmt {"; + qWarning().nospace() << indent() << " cond: {"; + inc(); + expression(s->cond); + dec(); + qWarning().nospace() << indent() << " }"; + qWarning().nospace() << indent() << " iftrue: BasicBlock(" << s->iftrue << ")"; + qWarning().nospace() << indent() << " iffalse: BasicBlock(" << s->iffalse << ")"; + qWarning().nospace() << indent() << "}"; +} + +void IRDump::visitRet(QDeclarativeJS::IR::Ret *s) +{ + qWarning().nospace() << indent() << "Ret:Stmt {"; + qWarning().nospace() << indent() << " type: " << typeName(s->type); + expression(s->expr); + qWarning().nospace() << indent() << "}"; +} +#endif + +} // end of namespace IR +} // end of namespace QDeclarativeJS + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v4/qdeclarativev4ir_p.h b/src/declarative/qml/v4/qdeclarativev4ir_p.h new file mode 100644 index 0000000000..93815e9154 --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4ir_p.h @@ -0,0 +1,546 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEV4IR_P_H +#define QDECLARATIVEV4IR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qdeclarativejsast_p.h> +#include <private/qdeclarativejsengine_p.h> +#include <private/qdeclarativeparser_p.h> +#include <private/qdeclarativeimport_p.h> +#include <private/qdeclarativeengine_p.h> +#include <private/qdeclarativev4compiler_p.h> + +#include <QtCore/qvector.h> + +// #define DEBUG_IR_STRUCTURE + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QDeclarativeType; + +namespace QDeclarativeJS { + +namespace IR { + +struct BasicBlock; +struct Function; +struct Module; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct Name; +struct Temp; +struct Unop; +struct Binop; +struct Call; + +// statements +struct Exp; +struct Move; +struct Jump; +struct CJump; +struct Ret; + +enum AluOp { + OpInvalid = 0, + + OpIfTrue, + OpNot, + OpUMinus, + OpUPlus, + OpCompl, + + OpBitAnd, + OpBitOr, + OpBitXor, + + OpAdd, + OpSub, + OpMul, + OpDiv, + OpMod, + + OpLShift, + OpRShift, + OpURShift, + + OpGt, + OpLt, + OpGe, + OpLe, + OpEqual, + OpNotEqual, + OpStrictEqual, + OpStrictNotEqual, + + OpAnd, + OpOr +}; +AluOp binaryOperator(int op); + +enum Type { + InvalidType, + UndefinedType, + NullType, + VoidType, + StringType, + UrlType, + AnchorLineType, + SGAnchorLineType, + AttachType, + ObjectType, + + FirstNumberType, + BoolType = FirstNumberType, + IntType, + RealType, + RealNaNType +}; +Type maxType(IR::Type left, IR::Type right); + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitUnop(Unop *) {} + virtual void visitBinop(Binop *) {} + virtual void visitCall(Call *) {} +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) {} + virtual void visitMove(Move *) {} + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *) {} + virtual void visitRet(Ret *) {} +}; + +struct Expr { + Type type; + + Expr(Type type): type(type) {} + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual void dump(QTextStream &out) = 0; +}; + +struct Const: Expr { + double value; + Const(Type type, double value): Expr(type), value(value) {} + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct String: Expr { + QString value; + String(const QString &value): Expr(StringType), value(value) {} + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out); + static QString escape(const QString &s); +}; + +enum BuiltinSymbol { + NoBuiltinSymbol, + MathSinBultinFunction, + MathCosBultinFunction, + MathRoundBultinFunction, + MathFloorBultinFunction, + + MathPIBuiltinConstant +}; + +struct Name: Expr { + enum Symbol { + Unbound, + IdObject, // This is a load of a id object. Storage will always be IdStorage + AttachType, // This is a load of an attached object + Object, // XXX what is this for? + Property, // This is a load of a regular property + Slot // XXX what is this for? + }; + + enum Storage { + MemberStorage, // This is a property of a previously fetched object + IdStorage, // This is a load of a id object. Symbol will always be IdObject + RootStorage, // This is a property of the root object + ScopeStorage // This is a property of the scope object + }; + + Name *base; + QString id; + Symbol symbol; + union { + void *ptr; + const QMetaObject *meta; + const QDeclarativeType *declarativeType; + const QDeclarativeParser::Object *idObject; + }; + int index; + Storage storage; + BuiltinSymbol builtin; + quint32 line; + quint32 column; + + Name(Name *base, Type type, const QString &id, Symbol symbol, quint32 line, quint32 column); + + inline bool is(Symbol s) const { return s == symbol; } + inline bool isNot(Symbol s) const { return s != symbol; } + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Temp: Expr { + int index; + Temp(Type type, int index): Expr(type), index(index) {} + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Unop: Expr { + AluOp op; + Expr *expr; + + Unop(AluOp op, Expr *expr) + : Expr(typeForOp(op, expr)), op(op), expr(expr) {} + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForOp(AluOp op, Expr *expr); +}; + +struct Binop: Expr { + AluOp op; + Expr *left; + Expr *right; + Binop(AluOp op, Expr *left, Expr *right) + : Expr(typeForOp(op, left, right)), op(op), left(left), right(right) {} + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForOp(AluOp op, Expr *left, Expr *right); +}; + +struct Call: Expr { + Expr *base; + QVector<Expr *> args; + + Call(Expr *base, const QVector<Expr *> &args) + : Expr(typeForFunction(base)), base(base), args(args) {} + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForFunction(Expr *base); +}; + +struct Stmt { + enum Mode { + HIR, + MIR + }; + + virtual ~Stmt() {} + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; +}; + +struct Exp: Stmt { + Expr *expr; + Exp(Expr *expr): expr(expr) {} + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; + Expr *source; + bool isMoveForReturn; + Move(Expr *target, Expr *source, bool isMoveForReturn): target(target), source(source), isMoveForReturn(isMoveForReturn) {} + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + Jump(BasicBlock *target): target(target) {} + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; + BasicBlock *iftrue; + BasicBlock *iffalse; + CJump(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + : cond(cond), iftrue(iftrue), iffalse(iffalse) {} + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Expr *expr; + Type type; + quint32 line; + quint32 column; + Ret(Expr *expr, Type type, quint32 line, quint32 column): expr(expr), type(type), line(line), column(column) {} + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Function { + Module *module; + const NameId *name; + int tempCount; + QVector<BasicBlock *> basicBlocks; + QVector<Expr *> temps; + + template <typename BB> inline BB i(BB i) { basicBlocks.append(i); return i; } + template <typename E> inline E e(E e) { temps.append(e); return e; } + + Function(Module *module, const NameId *name = 0): module(module), name(name), tempCount(0) {} + ~Function(); + + BasicBlock *newBasicBlock(); + + virtual void dump(QTextStream &out); +}; + +struct BasicBlock { + Function *function; + int index; + QVector<Stmt *> statements; + int offset; + + BasicBlock(Function *function, int index): function(function), index(index), offset(-1) {} + ~BasicBlock() { qDeleteAll(statements); } + + template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.last()->asTerminator() != 0) + return statements.last(); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + Temp *TEMP(Type type, int index); + Temp *TEMP(Type type); + + Expr *CONST(double value); + Expr *CONST(Type type, double value); + Expr *STRING(const QString &value); + + Name *NAME(const QString &id, quint32 line, quint32 column); + Name *NAME(Name *base, const QString &id, quint32 line, quint32 column); + Name *SYMBOL(Type type, const QString &id, const QMetaObject *meta, int index, Name::Storage storage, quint32 line, quint32 column); + Name *SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, int index, quint32 line, quint32 column); + Name *SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, int index, Name::Storage storage, quint32 line, quint32 column); + Name *ID_OBJECT(const QString &id, const QDeclarativeParser::Object *object, quint32 line, quint32 column); + Name *ATTACH_TYPE(const QString &id, const QDeclarativeType *attachType, Name::Storage storage, quint32 line, quint32 column); + + Expr *UNOP(AluOp op, Expr *expr); + Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *CALL(Expr *base, const QVector<Expr *> &args); + + Stmt *EXP(Expr *expr); + Stmt *MOVE(Expr *target, Expr *source, bool = false); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Expr *expr, Type type, quint32 line, quint32 column); + + virtual void dump(QTextStream &out); +}; + +struct Module { + QVector<Function *> functions; + + Module() {} + ~Module() { qDeleteAll(functions); } + + template <typename BB> inline BB i(BB i) { functions.append(i); return i; } + + Function *newFunction(const NameId *name = 0) { return i(new Function(this, name)); } + + virtual void dump(QTextStream &out); +}; + +#ifdef DEBUG_IR_STRUCTURE +struct IRDump : public ExprVisitor, + public StmtVisitor +{ +public: + IRDump(); + + void expression(QDeclarativeJS::IR::Expr *); + void basicblock(QDeclarativeJS::IR::BasicBlock *); + void statement(QDeclarativeJS::IR::Stmt *); + void function(QDeclarativeJS::IR::Function *); +protected: + + const char *indent(); + + // + // expressions + // + virtual void visitConst(QDeclarativeJS::IR::Const *e); + virtual void visitString(QDeclarativeJS::IR::String *e); + virtual void visitName(QDeclarativeJS::IR::Name *e); + virtual void visitTemp(QDeclarativeJS::IR::Temp *e); + virtual void visitUnop(QDeclarativeJS::IR::Unop *e); + virtual void visitBinop(QDeclarativeJS::IR::Binop *e); + virtual void visitCall(QDeclarativeJS::IR::Call *e); + + // + // statements + // + virtual void visitExp(QDeclarativeJS::IR::Exp *s); + virtual void visitMove(QDeclarativeJS::IR::Move *s); + virtual void visitJump(QDeclarativeJS::IR::Jump *s); + virtual void visitCJump(QDeclarativeJS::IR::CJump *s); + virtual void visitRet(QDeclarativeJS::IR::Ret *s); + +private: + int indentSize; + QByteArray indentData; + void inc(); + void dec(); +}; +#endif + +} // end of namespace IR + +} // end of namespace QDeclarativeJS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEV4IR_P_H diff --git a/src/declarative/qml/v4/qdeclarativev4irbuilder.cpp b/src/declarative/qml/v4/qdeclarativev4irbuilder.cpp new file mode 100644 index 0000000000..103d5ba0df --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4irbuilder.cpp @@ -0,0 +1,1315 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativev4irbuilder_p.h" + +#include <private/qdeclarativeglobalscriptclass_p.h> // For illegalNames +#include <private/qdeclarativeanchors_p_p.h> // For AnchorLine +#include <private/qsganchors_p_p.h> // For AnchorLine +#include <private/qdeclarativetypenamecache_p.h> + +DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER) + +QT_BEGIN_NAMESPACE + +using namespace QDeclarativeJS; + +static IR::Type irTypeFromVariantType(int t, QDeclarativeEnginePrivate *engine, const QMetaObject *meta) +{ + switch (t) { + case QMetaType::Bool: + return IR::BoolType; + + case QMetaType::Int: + return IR::IntType; + + case QMetaType::QReal: + return IR::RealType; + + case QMetaType::QString: + return IR::StringType; + + case QMetaType::QUrl: + return IR::UrlType; + + default: + if (t == qMetaTypeId<QDeclarativeAnchorLine>()) + return IR::AnchorLineType; + else if (t == qMetaTypeId<QSGAnchorLine>()) + return IR::SGAnchorLineType; + else if (const QMetaObject *m = engine->metaObjectForType(t)) { + meta = m; + return IR::ObjectType; + } + + return IR::InvalidType; + } +} + +QDeclarativeV4IRBuilder::QDeclarativeV4IRBuilder(const QDeclarativeV4Compiler::Expression *expr, + QDeclarativeEnginePrivate *engine) +: m_expression(expr), m_engine(engine), _module(0), _function(0), _block(0), _discard(false) +{ +} + +QDeclarativeJS::IR::Function * +QDeclarativeV4IRBuilder::operator()(QDeclarativeJS::IR::Module *module, + QDeclarativeJS::AST::Node *ast) +{ + bool discarded = false; + + qSwap(_module, module); + + IR::Function *function = _module->newFunction(); + IR::BasicBlock *block = function->newBasicBlock(); + + qSwap(_discard, discarded); + qSwap(_function, function); + qSwap(_block, block); + + ExprResult r; + AST::SourceLocation location; + if (AST::ExpressionNode *asExpr = ast->expressionCast()) { + r = expression(asExpr); + location = asExpr->firstSourceLocation(); + } else if (AST::Statement *asStmt = ast->statementCast()) { + r = statement(asStmt); + location = asStmt->firstSourceLocation(); + } + + //_block->MOVE(_block->TEMP(IR::InvalidType), r.code); + if (r.code) { + const QMetaObject *m = 0; + const IR::Type targetType = irTypeFromVariantType(m_expression->property->type, m_engine, m); + if (targetType != r.type()) { + IR::Expr *x = _block->TEMP(targetType); + _block->MOVE(x, r, true); + r.code = x; + } + _block->RET(r.code, targetType, location.startLine, location.startColumn); + } + + qSwap(_block, block); + qSwap(_function, function); + qSwap(_discard, discarded); + + qSwap(_module, module); + + return discarded?0:function; +} + +bool QDeclarativeV4IRBuilder::buildName(QStringList &name, + AST::Node *node, + QList<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; +} + +void QDeclarativeV4IRBuilder::discard() +{ + _discard = true; +} + +QDeclarativeV4IRBuilder::ExprResult +QDeclarativeV4IRBuilder::expression(AST::ExpressionNode *ast) +{ + ExprResult r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + + if (r.is(IR::InvalidType)) + discard(); + else { + Q_ASSERT(r.hint == r.format); + } + } + + return r; +} + +void QDeclarativeV4IRBuilder::condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +{ + if (! ast) + return; + ExprResult r(iftrue, iffalse); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + + if (r.format != ExprResult::cx) { + if (! r.code) + discard(); + + Q_ASSERT(r.hint == ExprResult::cx); + Q_ASSERT(r.format == ExprResult::ex); + + if (r.type() != IR::BoolType) { + IR::Temp *t = _block->TEMP(IR::BoolType); + _block->MOVE(t, r); + r = t; + } + + _block->CJUMP(_block->UNOP(IR::OpIfTrue, r), iftrue, iffalse); + } +} + +QDeclarativeV4IRBuilder::ExprResult +QDeclarativeV4IRBuilder::statement(AST::Statement *ast) +{ + ExprResult r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + + if (r.is(IR::InvalidType)) + discard(); + else { + Q_ASSERT(r.hint == r.format); + } + } + + return r; +} + +void QDeclarativeV4IRBuilder::sourceElement(AST::SourceElement *ast) +{ + accept(ast); +} + +void QDeclarativeV4IRBuilder::implicitCvt(ExprResult &expr, IR::Type type) +{ + if (expr.type() == type) + return; // nothing to do + + IR::Expr *x = _block->TEMP(type); + _block->MOVE(x, expr.code); + expr.code = x; +} + +// QML +bool QDeclarativeV4IRBuilder::visit(AST::UiProgram *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiImportList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiImport *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiPublicMember *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiSourceElement *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiObjectDefinition *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiObjectInitializer *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiObjectBinding *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiScriptBinding *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiArrayBinding *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiObjectMemberList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiArrayMemberList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiQualifiedId *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiSignature *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiFormalList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UiFormal *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + + +// JS +bool QDeclarativeV4IRBuilder::visit(AST::Program *ast) +{ + _function = _module->newFunction(); + _block = _function->newBasicBlock(); + accept(ast->elements); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::SourceElements *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::FunctionSourceElement *) +{ + return true; // look inside +} + +bool QDeclarativeV4IRBuilder::visit(AST::StatementSourceElement *) +{ + return true; // look inside +} + +// object literals +bool QDeclarativeV4IRBuilder::visit(AST::PropertyNameAndValueList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::IdentifierPropertyName *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::StringLiteralPropertyName *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::NumericLiteralPropertyName *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + + +// array literals +bool QDeclarativeV4IRBuilder::visit(AST::ElementList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::Elision *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + + +// function calls +bool QDeclarativeV4IRBuilder::visit(AST::ArgumentList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +// expressions +bool QDeclarativeV4IRBuilder::visit(AST::ObjectLiteral *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ArrayLiteral *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ThisExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::IdentifierExpression *ast) +{ + const quint32 line = ast->identifierToken.startLine; + const quint32 column = ast->identifierToken.startColumn; + + const QString name = ast->name->asString(); + + if (name.at(0) == QLatin1Char('u') && name.length() == 9 && name == QLatin1String("undefined")) { + _expr.code = _block->CONST(IR::UndefinedType, 0); // ### undefined value + } else if(m_engine->globalClass->illegalNames().contains(name) ) { + if (qmlVerboseCompiler()) qWarning() << "*** illegal symbol:" << name; + return false; + } else if (const QDeclarativeParser::Object *obj = m_expression->ids.value(name)) { + IR::Name *code = _block->ID_OBJECT(name, obj, line, column); + if (obj == m_expression->component) + code->storage = IR::Name::RootStorage; + _expr.code = code; + } else if (QDeclarativeTypeNameCache::Data *typeNameData = m_expression->importCache->data(name)) { + if (typeNameData->importedScriptIndex != -1) { + // We don't support invoking imported scripts + } else if (typeNameData->type) { + _expr.code = _block->ATTACH_TYPE(name, typeNameData->type, IR::Name::ScopeStorage, line, column); + } else if (typeNameData->typeNamespace) { + // We don't support namespaces + } else { + Q_ASSERT(!"Unreachable"); + } + } else { + bool found = false; + + if (m_expression->context != m_expression->component) { + // RootStorage is more efficient than ScopeStorage, so prefer that if they are the same + QDeclarativePropertyCache *cache = m_expression->context->synthCache; + const QMetaObject *metaObject = m_expression->context->metaObject(); + if (!cache) cache = m_engine->cache(metaObject); + + QDeclarativePropertyCache::Data *data = cache->property(name); + + if (data && data->revision != 0) { + if (qmlVerboseCompiler()) + qWarning() << "*** versioned symbol:" << name; + discard(); + return false; + } + + if (data && !(data->flags & QDeclarativePropertyCache::Data::IsFunction)) { + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, metaObject); + _expr.code = _block->SYMBOL(irType, name, metaObject, data->coreIndex, IR::Name::ScopeStorage, line, column); + found = true; + } + } + + if (!found) { + QDeclarativePropertyCache *cache = m_expression->component->synthCache; + const QMetaObject *metaObject = m_expression->component->metaObject(); + if (!cache) cache = m_engine->cache(metaObject); + + QDeclarativePropertyCache::Data *data = cache->property(name); + + if (data && data->revision != 0) { + if (qmlVerboseCompiler()) + qWarning() << "*** versioned symbol:" << name; + discard(); + return false; + } + + if (data && !(data->flags & QDeclarativePropertyCache::Data::IsFunction)) { + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, metaObject); + _expr.code = _block->SYMBOL(irType, name, metaObject, data->coreIndex, IR::Name::RootStorage, line, column); + found = true; + } + } + + if (!found && qmlVerboseCompiler()) + qWarning() << "*** unknown symbol:" << name; + } + + if (_expr.code && _expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + + if (_expr.type() != IR::BoolType) { + IR::Temp *t = _block->TEMP(IR::BoolType); + _block->MOVE(t, _expr); + _expr.code = t; + } + + _block->CJUMP(_expr.code, _expr.iftrue, _expr.iffalse); + _expr.code = 0; + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::NullExpression *) +{ + // ### TODO: cx format + _expr.code = _block->CONST(IR::NullType, 0); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::TrueLiteral *) +{ + // ### TODO: cx format + _expr.code = _block->CONST(IR::BoolType, 1); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::FalseLiteral *) +{ + // ### TODO: cx format + _expr.code = _block->CONST(IR::BoolType, 0); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::StringLiteral *ast) +{ + // ### TODO: cx format + _expr.code = _block->STRING(ast->value->asString()); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::NumericLiteral *ast) +{ + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + _block->JUMP(ast->value ? _expr.iftrue : _expr.iffalse); + } else { + _expr.code = _block->CONST(ast->value); + } + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::RegExpLiteral *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::NestedExpression *) +{ + return true; // the value of the nested expression +} + +bool QDeclarativeV4IRBuilder::visit(AST::ArrayMemberExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::FieldMemberExpression *ast) +{ + if (IR::Expr *left = expression(ast->base)) { + if (IR::Name *baseName = left->asName()) { + const quint32 line = ast->identifierToken.startLine; + const quint32 column = ast->identifierToken.startColumn; + + QString name = ast->name->asString(); + + switch(baseName->symbol) { + case IR::Name::Unbound: + break; + + case IR::Name::AttachType: + if (name.at(0).isUpper()) { + QByteArray utf8Name = name.toUtf8(); + const char *enumName = utf8Name.constData(); + + const QMetaObject *meta = baseName->declarativeType->metaObject(); + bool found = false; + for (int ii = 0; !found && ii < meta->enumeratorCount(); ++ii) { + QMetaEnum e = meta->enumerator(ii); + for (int jj = 0; !found && jj < e.keyCount(); ++jj) { + if (0 == strcmp(e.key(jj), enumName)) { + found = true; + _expr.code = _block->CONST(IR::IntType, e.value(jj)); + } + } + } + + if (!found && qmlVerboseCompiler()) + qWarning() << "*** unresolved enum:" + << (baseName->id + QLatin1String(".") + ast->name->asString()); + } else if(const QMetaObject *attachedMeta = baseName->declarativeType->attachedPropertiesType()) { + QDeclarativePropertyCache *cache = m_engine->cache(attachedMeta); + QDeclarativePropertyCache::Data *data = cache->property(name); + + if (!data || data->flags & QDeclarativePropertyCache::Data::IsFunction) + return false; // Don't support methods (or non-existing properties ;) + + if(!(data->flags & QDeclarativePropertyCache::Data::IsFinal)) { + if (qmlVerboseCompiler()) + qWarning() << "*** non-final attached property:" + << (baseName->id + QLatin1String(".") + ast->name->asString()); + return false; // We don't know enough about this property + } + + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, attachedMeta); + _expr.code = _block->SYMBOL(baseName, irType, name, attachedMeta, data->coreIndex, line, column); + } + break; + + case IR::Name::IdObject: { + const QDeclarativeParser::Object *idObject = baseName->idObject; + QDeclarativePropertyCache *cache = + idObject->synthCache?idObject->synthCache:m_engine->cache(idObject->metaObject()); + + QDeclarativePropertyCache::Data *data = cache->property(name); + + if (!data || data->flags & QDeclarativePropertyCache::Data::IsFunction) + return false; // Don't support methods (or non-existing properties ;) + + if (data->revision != 0) { + if (qmlVerboseCompiler()) + qWarning() << "*** versioned symbol:" << name; + discard(); + return false; + } + + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, idObject->metaObject()); + _expr.code = _block->SYMBOL(baseName, irType, name, + idObject->metaObject(), data->coreIndex, line, column); + } + break; + + case IR::Name::Property: + if (baseName->type == IR::ObjectType) { + const QMetaObject *m = + m_engine->metaObjectForType(baseName->meta->property(baseName->index).userType()); + QDeclarativePropertyCache *cache = m_engine->cache(m); + + QDeclarativePropertyCache::Data *data = cache->property(name); + + if (!data || data->flags & QDeclarativePropertyCache::Data::IsFunction) + return false; // Don't support methods (or non-existing properties ;) + + if(!(data->flags & QDeclarativePropertyCache::Data::IsFinal)) { + if (qmlVerboseCompiler()) + qWarning() << "*** non-final property access:" + << (baseName->id + QLatin1String(".") + ast->name->asString()); + return false; // We don't know enough about this property + } + + IR::Type irType = irTypeFromVariantType(data->propType, m_engine, baseName->meta); + _expr.code = _block->SYMBOL(baseName, irType, name, + baseName->meta, data->coreIndex, line, column); + } + break; + + case IR::Name::Object: + case IR::Name::Slot: + break; + } + } + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::NewMemberExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::NewExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::CallExpression *ast) +{ + QStringList names; + QList<AST::ExpressionNode *> nameNodes; + if (buildName(names, ast->base, &nameNodes)) { + //ExprResult base = expression(ast->base); + const QString id = names.join(QLatin1String(".")); + const quint32 line = nameNodes.last()->firstSourceLocation().startLine; + const quint32 column = nameNodes.last()->firstSourceLocation().startColumn; + IR::Expr *base = _block->NAME(id, line, column); + + QVector<IR::Expr *> args; + for (AST::ArgumentList *it = ast->arguments; it; it = it->next) + args.append(expression(it->expression)); + + IR::Temp *r = _block->TEMP(IR::InvalidType); + IR::Expr *call = _block->CALL(base, args); + _block->MOVE(r, call); + r->type = call->type; + _expr.code = r; + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::PostIncrementExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::PostDecrementExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::DeleteExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::VoidExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::TypeOfExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::PreIncrementExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::PreDecrementExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UnaryPlusExpression *ast) +{ + ExprResult expr = expression(ast->expression); + if (expr.isNot(IR::InvalidType)) { + if (expr.code->asConst() != 0) { + _expr = expr; + return false; + } + + IR::Expr *code = _block->UNOP(IR::OpUPlus, expr); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr, code); + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::UnaryMinusExpression *ast) +{ + ExprResult expr = expression(ast->expression); + if (expr.isNot(IR::InvalidType)) { + if (IR::Const *c = expr.code->asConst()) { + _expr = expr; + _expr.code = _block->CONST(-c->value); + return false; + } + + IR::Expr *code = _block->UNOP(IR::OpUMinus, expr); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr, code); + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::TildeExpression *ast) +{ + ExprResult expr = expression(ast->expression); + if (expr.isNot(IR::InvalidType)) { + if (IR::Const *c = expr.code->asConst()) { + _expr = expr; + _expr.code = _block->CONST(~int(c->value)); + return false; + } + IR::Expr *code = _block->UNOP(IR::OpCompl, expr); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr, code); + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::NotExpression *ast) +{ + ExprResult expr = expression(ast->expression); + + if (expr.isNot(IR::InvalidType)) { + if (IR::Const *c = expr.code->asConst()) { + _expr = expr; + _expr.code = _block->CONST(!c->value); + return false; + } + + IR::Expr *code = _block->UNOP(IR::OpNot, expr); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr, code); + + } else if (expr.hint == ExprResult::cx) { + expr.format = ExprResult::cx; + _block->CJUMP(_block->UNOP(IR::OpNot, expr), _expr.iftrue, _expr.iffalse); + return false; + } + + return false; +} + +void QDeclarativeV4IRBuilder::binop(AST::BinaryExpression *ast, ExprResult left, ExprResult right) +{ + if (IR::Type t = maxType(left.type(), right.type())) { + implicitCvt(left, t); + implicitCvt(right, t); + + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + _block->CJUMP(_block->BINOP(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); + } else { + IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr.code, code); + } + } +} + +bool QDeclarativeV4IRBuilder::visit(AST::BinaryExpression *ast) +{ + switch (ast->op) { + case QSOperator::And: { + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + + Q_ASSERT(_expr.iffalse != 0); + Q_ASSERT(_expr.iftrue != 0); + + IR::BasicBlock *iftrue = _function->newBasicBlock(); + condition(ast->left, iftrue, _expr.iffalse); + + _block = iftrue; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + condition(ast->left, iftrue, iffalse); + + IR::Temp *r = _block->TEMP(IR::InvalidType); + + _block = iffalse; + _block->MOVE(r, _block->CONST(0)); // ### use the right null value + _block->JUMP(endif); + + _block = iftrue; + ExprResult right = expression(ast->right); + _block->MOVE(r, right); + _block->JUMP(endif); + + _block = endif; + + r->type = right.type(); // ### not exactly, it can be IR::BoolType. + _expr.code = r; + } + } break; + + case QSOperator::Or: { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + ExprResult left = expression(ast->left); + IR::Temp *r = _block->TEMP(left.type()); + _block->MOVE(r, left); + + IR::Expr *cond = r; + if (r->type != IR::BoolType) { + cond = _block->TEMP(IR::BoolType); + _block->MOVE(cond, r); + } + + _block->CJUMP(_block->UNOP(IR::OpNot, cond), iftrue, endif); + + _block = iftrue; + ExprResult right = expression(ast->right); + _block->MOVE(r, right); + + if (left.type() != right.type()) + discard(); + + _expr.code = r; + + _block = endif; + } break; + + case QSOperator::Lt: + case QSOperator::Gt: + case QSOperator::Le: + case QSOperator::Ge: { + ExprResult left = expression(ast->left); + ExprResult right = expression(ast->right); + if (left.type() == IR::StringType && right.type() == IR::StringType) { + binop(ast, left, right); + } else if (left.isValid() && right.isValid()) { + implicitCvt(left, IR::RealType); + implicitCvt(right, IR::RealType); + binop(ast, left, right); + } + } break; + + case QSOperator::NotEqual: + case QSOperator::Equal: { + ExprResult left = expression(ast->left); + ExprResult right = expression(ast->right); + if ((left.type() == IR::NullType || left.type() == IR::UndefinedType) && + (right.type() == IR::NullType || right.type() == IR::UndefinedType)) { + const bool isEq = ast->op == QSOperator::Equal; + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + _block->JUMP(isEq ? _expr.iftrue : _expr.iffalse); + } else { + _expr.code = _block->CONST(IR::BoolType, isEq ? 1 : 0); + } + } else if ((left.type() == IR::StringType && right.type() >= IR::FirstNumberType) || + (left.type() >= IR::FirstNumberType && right.type() == IR::StringType)) { + implicitCvt(left, IR::RealType); + implicitCvt(right, IR::RealType); + binop(ast, left, right); + } else if (left.type() == IR::BoolType || right.type() == IR::BoolType) { + implicitCvt(left, IR::BoolType); + implicitCvt(right, IR::BoolType); + } else if (left.isValid() && right.isValid()) { + binop(ast, left, right); + } + } break; + + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: { + ExprResult left = expression(ast->left); + ExprResult right = expression(ast->right); + if (left.type() == right.type()) { + binop(ast, left, right); + } else if (left.type() >= IR::BoolType && right.type() >= IR::BoolType) { + // left and right have numeric type (int or real) + binop(ast, left, right); + } else if (left.isValid() && right.isValid()) { + const bool isEq = ast->op == QSOperator::StrictEqual; + if (_expr.hint == ExprResult::cx) { + _expr.format = ExprResult::cx; + _block->JUMP(isEq ? _expr.iftrue : _expr.iffalse); + } else { + _expr.code = _block->CONST(IR::BoolType, isEq ? 1 : 0); + } + } + } break; + + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + case QSOperator::LShift: + case QSOperator::RShift: + case QSOperator::URShift: { + ExprResult left = expression(ast->left); + if (left.is(IR::InvalidType)) + return false; + + ExprResult right = expression(ast->right); + if (right.is(IR::InvalidType)) + return false; + + implicitCvt(left, IR::IntType); + implicitCvt(right, IR::IntType); + + IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr.code, code); + + } break; + + case QSOperator::Add: { + ExprResult left = expression(ast->left); + if (left.is(IR::InvalidType)) + return false; + + ExprResult right = expression(ast->right); + if (right.is(IR::InvalidType)) + return false; + + if (left.isPrimitive() && right.isPrimitive()) { + if (left.type() == IR::StringType || right.type() == IR::StringType) { + implicitCvt(left, IR::StringType); + implicitCvt(right, IR::StringType); + } + binop(ast, left, right); + } + } break; + + case QSOperator::Div: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::Sub: { + ExprResult left = expression(ast->left); + if (left.is(IR::InvalidType)) + return false; + + ExprResult right = expression(ast->right); + if (right.is(IR::InvalidType)) + return false; + + IR::Type t = maxType(left.type(), right.type()); + if (t >= IR::FirstNumberType) { + implicitCvt(left, IR::RealType); + implicitCvt(right, IR::RealType); + + IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); + _expr.code = _block->TEMP(code->type); + _block->MOVE(_expr.code, code); + } + } break; + + case QSOperator::In: + case QSOperator::InstanceOf: + case QSOperator::Assign: + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: + // yup, we don't do those. + break; + } // switch + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ConditionalExpression *ast) +{ + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + condition(ast->expression, iftrue, iffalse); + + IR::Temp *r = _block->TEMP(IR::InvalidType); + + qSwap(_block, iftrue); + ExprResult ok = expression(ast->ok); + _block->MOVE(r, ok); + _block->JUMP(endif); + qSwap(_block, iftrue); + + qSwap(_block, iffalse); + ExprResult ko = expression(ast->ko); + _block->MOVE(r, ko); + _block->JUMP(endif); + qSwap(_block, iffalse); + + r->type = maxType(ok.type(), ko.type()); + _expr.code = r; + + _block = endif; + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::Expression *ast) +{ + _block->EXP(expression(ast->left)); + _expr = expression(ast->right); + + return false; +} + + +// statements +bool QDeclarativeV4IRBuilder::visit(AST::Block *ast) +{ + if (ast->statements && ! ast->statements->next) { + // we have one and only one statement + accept(ast->statements->statement); + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::StatementList *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::VariableStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::VariableDeclarationList *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::VariableDeclaration *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::EmptyStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ExpressionStatement *ast) +{ + if (ast->expression) { + // return the value of this expression + return true; + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::IfStatement *ast) +{ + if (! ast->ko) { + // This is an if statement without an else branch. + discard(); + } else { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + condition(ast->expression, iftrue, iffalse); + + IR::Temp *r = _block->TEMP(IR::InvalidType); + + qSwap(_block, iftrue); + ExprResult ok = statement(ast->ok); + _block->MOVE(r, ok); + _block->JUMP(endif); + qSwap(_block, iftrue); + + qSwap(_block, iffalse); + ExprResult ko = statement(ast->ko); + _block->MOVE(r, ko); + _block->JUMP(endif); + qSwap(_block, iffalse); + + r->type = maxType(ok.type(), ko.type()); + _expr.code = r; + + _block = endif; + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::DoWhileStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::WhileStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ForStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::LocalForStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ForEachStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::LocalForEachStatement *) +{ + discard(); + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ContinueStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::BreakStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ReturnStatement *ast) +{ + if (ast->expression) { + // return the value of the expression + return true; + } + + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::WithStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::SwitchStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::CaseBlock *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::CaseClauses *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::CaseClause *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::DefaultClause *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::LabelledStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::ThrowStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::TryStatement *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::Catch *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::Finally *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::FunctionDeclaration *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::FunctionExpression *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::FormalParameterList *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::FunctionBody *) +{ + return false; +} + +bool QDeclarativeV4IRBuilder::visit(AST::DebuggerStatement *) +{ + return false; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v4/qdeclarativev4irbuilder_p.h b/src/declarative/qml/v4/qdeclarativev4irbuilder_p.h new file mode 100644 index 0000000000..f0bfd6624c --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4irbuilder_p.h @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEV4IRBUILDER_P_H +#define QDECLARATIVEV4IRBUILDER_P_H + +#include <QtCore/qglobal.h> + +#include "qdeclarativev4ir_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeV4IRBuilder : public QDeclarativeJS::AST::Visitor +{ +public: + QDeclarativeV4IRBuilder(const QDeclarativeV4Compiler::Expression *, QDeclarativeEnginePrivate *); + + QDeclarativeJS::IR::Function *operator()(QDeclarativeJS::IR::Module *, QDeclarativeJS::AST::Node *); + +protected: + struct ExprResult { + enum Format { + ex, // expression + cx // condition + }; + + QDeclarativeJS::IR::Expr *code; + QDeclarativeJS::IR::BasicBlock *iftrue; + QDeclarativeJS::IR::BasicBlock *iffalse; + Format hint; // requested format + Format format; // instruction format + + ExprResult(QDeclarativeJS::IR::Expr *expr = 0) + : code(expr), iftrue(0), iffalse(0), hint(ex), format(ex) {} + + ExprResult(QDeclarativeJS::IR::BasicBlock *iftrue, QDeclarativeJS::IR::BasicBlock *iffalse) + : code(0), iftrue(iftrue), iffalse(iffalse), hint(cx), format(ex) {} + + inline QDeclarativeJS::IR::Type type() const { return code ? code->type : QDeclarativeJS::IR::InvalidType; } + + inline QDeclarativeJS::IR::Expr *get() const { return code; } + inline operator QDeclarativeJS::IR::Expr *() const { return get(); } + inline QDeclarativeJS::IR::Expr *operator->() const { return get(); } + inline bool isValid() const { return code ? code->type != QDeclarativeJS::IR::InvalidType : false; } + inline bool is(QDeclarativeJS::IR::Type t) const { return type() == t; } + inline bool isNot(QDeclarativeJS::IR::Type t) const { return type() != t; } + + bool isPrimitive() const { + switch (type()) { + case QDeclarativeJS::IR::UndefinedType: // ### TODO + case QDeclarativeJS::IR::NullType: // ### TODO + case QDeclarativeJS::IR::UrlType: // ### TODO + return false; + + case QDeclarativeJS::IR::StringType: + case QDeclarativeJS::IR::BoolType: + case QDeclarativeJS::IR::IntType: + case QDeclarativeJS::IR::RealType: + case QDeclarativeJS::IR::RealNaNType: + return true; + + default: + return false; + } // switch + } + }; + + inline void accept(QDeclarativeJS::AST::Node *ast) { QDeclarativeJS::AST::Node::accept(ast, this); } + + ExprResult expression(QDeclarativeJS::AST::ExpressionNode *ast); + ExprResult statement(QDeclarativeJS::AST::Statement *ast); + void sourceElement(QDeclarativeJS::AST::SourceElement *ast); + void condition(QDeclarativeJS::AST::ExpressionNode *ast, QDeclarativeJS::IR::BasicBlock *iftrue, QDeclarativeJS::IR::BasicBlock *iffalse); + void binop(QDeclarativeJS::AST::BinaryExpression *ast, ExprResult left, ExprResult right); + + void implicitCvt(ExprResult &expr, QDeclarativeJS::IR::Type type); + + // QML + virtual bool visit(QDeclarativeJS::AST::UiProgram *ast); + virtual bool visit(QDeclarativeJS::AST::UiImportList *ast); + virtual bool visit(QDeclarativeJS::AST::UiImport *ast); + virtual bool visit(QDeclarativeJS::AST::UiPublicMember *ast); + virtual bool visit(QDeclarativeJS::AST::UiSourceElement *ast); + virtual bool visit(QDeclarativeJS::AST::UiObjectDefinition *ast); + virtual bool visit(QDeclarativeJS::AST::UiObjectInitializer *ast); + virtual bool visit(QDeclarativeJS::AST::UiObjectBinding *ast); + virtual bool visit(QDeclarativeJS::AST::UiScriptBinding *ast); + virtual bool visit(QDeclarativeJS::AST::UiArrayBinding *ast); + virtual bool visit(QDeclarativeJS::AST::UiObjectMemberList *ast); + virtual bool visit(QDeclarativeJS::AST::UiArrayMemberList *ast); + virtual bool visit(QDeclarativeJS::AST::UiQualifiedId *ast); + virtual bool visit(QDeclarativeJS::AST::UiSignature *ast); + virtual bool visit(QDeclarativeJS::AST::UiFormalList *ast); + virtual bool visit(QDeclarativeJS::AST::UiFormal *ast); + + // JS + virtual bool visit(QDeclarativeJS::AST::Program *ast); + virtual bool visit(QDeclarativeJS::AST::SourceElements *ast); + virtual bool visit(QDeclarativeJS::AST::FunctionSourceElement *ast); + virtual bool visit(QDeclarativeJS::AST::StatementSourceElement *ast); + + // object literals + virtual bool visit(QDeclarativeJS::AST::PropertyNameAndValueList *ast); + virtual bool visit(QDeclarativeJS::AST::IdentifierPropertyName *ast); + virtual bool visit(QDeclarativeJS::AST::StringLiteralPropertyName *ast); + virtual bool visit(QDeclarativeJS::AST::NumericLiteralPropertyName *ast); + + // array literals + virtual bool visit(QDeclarativeJS::AST::ElementList *ast); + virtual bool visit(QDeclarativeJS::AST::Elision *ast); + + // function calls + virtual bool visit(QDeclarativeJS::AST::ArgumentList *ast); + + // expressions + virtual bool visit(QDeclarativeJS::AST::ObjectLiteral *ast); + virtual bool visit(QDeclarativeJS::AST::ArrayLiteral *ast); + virtual bool visit(QDeclarativeJS::AST::ThisExpression *ast); + virtual bool visit(QDeclarativeJS::AST::IdentifierExpression *ast); + virtual bool visit(QDeclarativeJS::AST::NullExpression *ast); + virtual bool visit(QDeclarativeJS::AST::TrueLiteral *ast); + virtual bool visit(QDeclarativeJS::AST::FalseLiteral *ast); + virtual bool visit(QDeclarativeJS::AST::StringLiteral *ast); + virtual bool visit(QDeclarativeJS::AST::NumericLiteral *ast); + virtual bool visit(QDeclarativeJS::AST::RegExpLiteral *ast); + virtual bool visit(QDeclarativeJS::AST::NestedExpression *ast); + virtual bool visit(QDeclarativeJS::AST::ArrayMemberExpression *ast); + virtual bool visit(QDeclarativeJS::AST::FieldMemberExpression *ast); + virtual bool visit(QDeclarativeJS::AST::NewMemberExpression *ast); + virtual bool visit(QDeclarativeJS::AST::NewExpression *ast); + virtual bool visit(QDeclarativeJS::AST::CallExpression *ast); + virtual bool visit(QDeclarativeJS::AST::PostIncrementExpression *ast); + virtual bool visit(QDeclarativeJS::AST::PostDecrementExpression *ast); + virtual bool visit(QDeclarativeJS::AST::DeleteExpression *ast); + virtual bool visit(QDeclarativeJS::AST::VoidExpression *ast); + virtual bool visit(QDeclarativeJS::AST::TypeOfExpression *ast); + virtual bool visit(QDeclarativeJS::AST::PreIncrementExpression *ast); + virtual bool visit(QDeclarativeJS::AST::PreDecrementExpression *ast); + virtual bool visit(QDeclarativeJS::AST::UnaryPlusExpression *ast); + virtual bool visit(QDeclarativeJS::AST::UnaryMinusExpression *ast); + virtual bool visit(QDeclarativeJS::AST::TildeExpression *ast); + virtual bool visit(QDeclarativeJS::AST::NotExpression *ast); + virtual bool visit(QDeclarativeJS::AST::BinaryExpression *ast); + virtual bool visit(QDeclarativeJS::AST::ConditionalExpression *ast); + virtual bool visit(QDeclarativeJS::AST::Expression *ast); + + // statements + virtual bool visit(QDeclarativeJS::AST::Block *ast); + virtual bool visit(QDeclarativeJS::AST::StatementList *ast); + virtual bool visit(QDeclarativeJS::AST::VariableStatement *ast); + virtual bool visit(QDeclarativeJS::AST::VariableDeclarationList *ast); + virtual bool visit(QDeclarativeJS::AST::VariableDeclaration *ast); + virtual bool visit(QDeclarativeJS::AST::EmptyStatement *ast); + virtual bool visit(QDeclarativeJS::AST::ExpressionStatement *ast); + virtual bool visit(QDeclarativeJS::AST::IfStatement *ast); + virtual bool visit(QDeclarativeJS::AST::DoWhileStatement *ast); + virtual bool visit(QDeclarativeJS::AST::WhileStatement *ast); + virtual bool visit(QDeclarativeJS::AST::ForStatement *ast); + virtual bool visit(QDeclarativeJS::AST::LocalForStatement *ast); + virtual bool visit(QDeclarativeJS::AST::ForEachStatement *ast); + virtual bool visit(QDeclarativeJS::AST::LocalForEachStatement *ast); + virtual bool visit(QDeclarativeJS::AST::ContinueStatement *ast); + virtual bool visit(QDeclarativeJS::AST::BreakStatement *ast); + virtual bool visit(QDeclarativeJS::AST::ReturnStatement *ast); + virtual bool visit(QDeclarativeJS::AST::WithStatement *ast); + virtual bool visit(QDeclarativeJS::AST::SwitchStatement *ast); + virtual bool visit(QDeclarativeJS::AST::CaseBlock *ast); + virtual bool visit(QDeclarativeJS::AST::CaseClauses *ast); + virtual bool visit(QDeclarativeJS::AST::CaseClause *ast); + virtual bool visit(QDeclarativeJS::AST::DefaultClause *ast); + virtual bool visit(QDeclarativeJS::AST::LabelledStatement *ast); + virtual bool visit(QDeclarativeJS::AST::ThrowStatement *ast); + virtual bool visit(QDeclarativeJS::AST::TryStatement *ast); + virtual bool visit(QDeclarativeJS::AST::Catch *ast); + virtual bool visit(QDeclarativeJS::AST::Finally *ast); + virtual bool visit(QDeclarativeJS::AST::FunctionDeclaration *ast); + virtual bool visit(QDeclarativeJS::AST::FunctionExpression *ast); + virtual bool visit(QDeclarativeJS::AST::FormalParameterList *ast); + virtual bool visit(QDeclarativeJS::AST::FunctionBody *ast); + virtual bool visit(QDeclarativeJS::AST::DebuggerStatement *ast); + +private: + bool buildName(QStringList &name, QDeclarativeJS::AST::Node *node, + QList<QDeclarativeJS::AST::ExpressionNode *> *nodes); + void discard(); + + const QDeclarativeV4Compiler::Expression *m_expression; + QDeclarativeEnginePrivate *m_engine; + + QDeclarativeJS::IR::Module *_module; + QDeclarativeJS::IR::Function *_function; + QDeclarativeJS::IR::BasicBlock *_block; + bool _discard; + + ExprResult _expr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEV4IRBUILDER_P_H diff --git a/src/declarative/qml/v4/qdeclarativev4program_p.h b/src/declarative/qml/v4/qdeclarativev4program_p.h new file mode 100644 index 0000000000..d036bd6f73 --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4program_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEV4PROGRAM_P_H +#define QDECLARATIVEV4PROGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qdeclarativev4instruction_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +struct QDeclarativeV4Program { + quint32 bindings; + quint32 dataLength; + quint32 signalTableOffset; + quint32 exceptionDataOffset; + quint16 subscriptions; + quint16 identifiers; + quint16 instructionCount; + + struct BindingReference { + quint32 binding; + quint32 blockMask; + }; + + struct BindingReferenceList { + quint32 count; + BindingReference bindings[]; + }; + + inline const char *data() const; + inline const char *instructions() const; + inline BindingReferenceList *signalTable(int signalIndex) const; +}; + +enum QDeclarativeRegisterType { + UndefinedType, + QObjectStarType, + QRealType, + IntType, + BoolType, + + PODValueType, + + FirstCleanupType, + QStringType = FirstCleanupType, + QUrlType, + QVariantType, +}; + +const char *QDeclarativeV4Program::data() const +{ + return ((const char *)this) + sizeof(QDeclarativeV4Program); +} + +const char *QDeclarativeV4Program::instructions() const +{ + return (const char *)(data() + dataLength); +} + +QDeclarativeV4Program::BindingReferenceList *QDeclarativeV4Program::signalTable(int signalIndex) const +{ + quint32 *signalTable = (quint32 *)(data() + signalTableOffset); + return (BindingReferenceList *)(signalTable + signalTable[signalIndex]); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEV4PROGRAM_P_H + diff --git a/src/declarative/qml/v4/v4.pri b/src/declarative/qml/v4/v4.pri new file mode 100644 index 0000000000..085f0ae443 --- /dev/null +++ b/src/declarative/qml/v4/v4.pri @@ -0,0 +1,17 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qdeclarativev4compiler_p.h \ + $$PWD/qdeclarativev4compiler_p_p.h \ + $$PWD/qdeclarativev4ir_p.h \ + $$PWD/qdeclarativev4irbuilder_p.h \ + $$PWD/qdeclarativev4instruction_p.h \ + $$PWD/qdeclarativev4bindings_p.h \ + $$PWD/qdeclarativev4program_p.h \ + +SOURCES += \ + $$PWD/qdeclarativev4compiler.cpp \ + $$PWD/qdeclarativev4ir.cpp \ + $$PWD/qdeclarativev4irbuilder.cpp \ + $$PWD/qdeclarativev4instruction.cpp \ + $$PWD/qdeclarativev4bindings.cpp \ |