diff options
Diffstat (limited to 'src/declarative/qml/v4')
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4bindings.cpp | 1530 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4bindings_p.h | 92 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4compiler.cpp | 1340 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4compiler_p.h | 104 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4compiler_p_p.h | 184 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4instruction.cpp | 559 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4instruction_p.h | 444 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4ir.cpp | 832 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4ir_p.h | 546 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4irbuilder.cpp | 1315 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4irbuilder_p.h | 242 | ||||
-rw-r--r-- | src/declarative/qml/v4/qdeclarativev4program_p.h | 122 | ||||
-rw-r--r-- | src/declarative/qml/v4/v4.pri | 17 |
13 files changed, 7327 insertions, 0 deletions
diff --git a/src/declarative/qml/v4/qdeclarativev4bindings.cpp b/src/declarative/qml/v4/qdeclarativev4bindings.cpp new file mode 100644 index 0000000000..5ef293de89 --- /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_V4_INSTR(QML_V4_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_V4_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_V4_BEGIN_INSTR(Noop, common) + QML_V4_END_INSTR(Noop, common) + + QML_V4_BEGIN_INSTR(BindingId, id) + bindingLine = instr->id.line; + bindingColumn = instr->id.column; + QML_V4_END_INSTR(BindingId, id) + + QML_V4_BEGIN_INSTR(SubscribeId, subscribeop) + subscribeId(context, instr->subscribeop.index, instr->subscribeop.offset); + QML_V4_END_INSTR(SubscribeId, subscribeop) + + QML_V4_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_V4_END_INSTR(Subscribe, subscribeop) + + QML_V4_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_V4_END_INSTR(FetchAndSubscribe, fetchAndSubscribe) + + QML_V4_BEGIN_INSTR(LoadId, load) + registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data()); + QML_V4_END_INSTR(LoadId, load) + + QML_V4_BEGIN_INSTR(LoadScope, load) + registers[instr->load.reg].setQObject(scope); + QML_V4_END_INSTR(LoadScope, load) + + QML_V4_BEGIN_INSTR(LoadRoot, load) + registers[instr->load.reg].setQObject(context->contextObject); + QML_V4_END_INSTR(LoadRoot, load) + + QML_V4_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_V4_END_INSTR(LoadAttached, attached) + + QML_V4_BEGIN_INSTR(UnaryNot, unaryop) + { + registers[instr->unaryop.output].setbool(!registers[instr->unaryop.src].getbool()); + } + QML_V4_END_INSTR(UnaryNot, unaryop) + + QML_V4_BEGIN_INSTR(UnaryMinusReal, unaryop) + { + registers[instr->unaryop.output].setqreal(-registers[instr->unaryop.src].getqreal()); + } + QML_V4_END_INSTR(UnaryMinusReal, unaryop) + + QML_V4_BEGIN_INSTR(UnaryMinusInt, unaryop) + { + registers[instr->unaryop.output].setint(-registers[instr->unaryop.src].getint()); + } + QML_V4_END_INSTR(UnaryMinusInt, unaryop) + + QML_V4_BEGIN_INSTR(UnaryPlusReal, unaryop) + { + registers[instr->unaryop.output].setqreal(+registers[instr->unaryop.src].getqreal()); + } + QML_V4_END_INSTR(UnaryPlusReal, unaryop) + + QML_V4_BEGIN_INSTR(UnaryPlusInt, unaryop) + { + registers[instr->unaryop.output].setint(+registers[instr->unaryop.src].getint()); + } + QML_V4_END_INSTR(UnaryPlusInt, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertBoolToInt, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertBoolToReal, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertBoolToString, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertIntToBool, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertIntToReal, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertIntToString, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertRealToBool, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertRealToInt, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertRealToString, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertStringToBool, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertStringToInt, unaryop) + + QML_V4_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_V4_END_INSTR(ConvertStringToReal, unaryop) + + QML_V4_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_V4_END_INSTR(MathSinReal, unaryop) + + QML_V4_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_V4_END_INSTR(MathCosReal, unaryop) + + QML_V4_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_V4_END_INSTR(MathRoundReal, unaryop) + + QML_V4_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_V4_END_INSTR(MathFloorReal, unaryop) + + QML_V4_BEGIN_INSTR(MathPIReal, unaryop) + { + static const qsreal qmlPI = 2.0 * qAsin(1.0); + Register &output = registers[instr->unaryop.output]; + output.setqreal(qmlPI); + } + QML_V4_END_INSTR(MathPIReal, unaryop) + + QML_V4_BEGIN_INSTR(Real, real_value) + registers[instr->real_value.reg].setqreal(instr->real_value.value); + QML_V4_END_INSTR(Real, real_value) + + QML_V4_BEGIN_INSTR(Int, int_value) + registers[instr->int_value.reg].setint(instr->int_value.value); + QML_V4_END_INSTR(Int, int_value) + + QML_V4_BEGIN_INSTR(Bool, bool_value) + registers[instr->bool_value.reg].setbool(instr->bool_value.value); + QML_V4_END_INSTR(Bool, bool_value) + + QML_V4_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_V4_END_INSTR(String, string_value) + + QML_V4_BEGIN_INSTR(EnableV4Test, string_value) + { + testBindingSource = new QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); + testBinding = true; + } + QML_V4_END_INSTR(String, string_value) + + QML_V4_BEGIN_INSTR(BitAndInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() & + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(BitAndInt, binaryop) + + QML_V4_BEGIN_INSTR(BitOrInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() | + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(BitAndInt, binaryop) + + QML_V4_BEGIN_INSTR(BitXorInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() ^ + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(BitXorInt, binaryop) + + QML_V4_BEGIN_INSTR(AddReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() + + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(AddReal, binaryop) + + QML_V4_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_V4_END_INSTR(AddString, binaryop) + + QML_V4_BEGIN_INSTR(SubReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() - + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(SubReal, binaryop) + + QML_V4_BEGIN_INSTR(MulReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() * + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(MulReal, binaryop) + + QML_V4_BEGIN_INSTR(DivReal, binaryop) + { + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() / + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(DivReal, binaryop) + + QML_V4_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_V4_END_INSTR(ModInt, binaryop) + + QML_V4_BEGIN_INSTR(LShiftInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() << + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(LShiftInt, binaryop) + + QML_V4_BEGIN_INSTR(RShiftInt, binaryop) + { + registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() >> + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(RShiftInt, binaryop) + + QML_V4_BEGIN_INSTR(URShiftInt, binaryop) + { + registers[instr->binaryop.output].setint((unsigned)registers[instr->binaryop.left].getint() >> + registers[instr->binaryop.right].getint()); + } + QML_V4_END_INSTR(URShiftInt, binaryop) + + QML_V4_BEGIN_INSTR(GtReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() > + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(GtReal, binaryop) + + QML_V4_BEGIN_INSTR(LtReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() < + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(LtReal, binaryop) + + QML_V4_BEGIN_INSTR(GeReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() >= + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(GeReal, binaryop) + + QML_V4_BEGIN_INSTR(LeReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() <= + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(LeReal, binaryop) + + QML_V4_BEGIN_INSTR(EqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() == + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(EqualReal, binaryop) + + QML_V4_BEGIN_INSTR(NotEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() != + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(NotEqualReal, binaryop) + + QML_V4_BEGIN_INSTR(StrictEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() == + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(StrictEqualReal, binaryop) + + QML_V4_BEGIN_INSTR(StrictNotEqualReal, binaryop) + { + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() != + registers[instr->binaryop.right].getqreal()); + } + QML_V4_END_INSTR(StrictNotEqualReal, binaryop) + + QML_V4_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_V4_END_INSTR(GtString, binaryop) + + QML_V4_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_V4_END_INSTR(LtString, binaryop) + + QML_V4_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_V4_END_INSTR(GeString, binaryop) + + QML_V4_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_V4_END_INSTR(LeString, binaryop) + + QML_V4_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_V4_END_INSTR(EqualString, binaryop) + + QML_V4_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_V4_END_INSTR(NotEqualString, binaryop) + + QML_V4_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_V4_END_INSTR(StrictEqualString, binaryop) + + QML_V4_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_V4_END_INSTR(StrictNotEqualString, binaryop) + + QML_V4_BEGIN_INSTR(NewString, construct) + { + Register &output = registers[instr->construct.reg]; + new (output.getstringptr()) QString; + STRING_REGISTER(instr->construct.reg); + } + QML_V4_END_INSTR(NewString, construct) + + QML_V4_BEGIN_INSTR(NewUrl, construct) + { + Register &output = registers[instr->construct.reg]; + new (output.geturlptr()) QUrl; + URL_REGISTER(instr->construct.reg); + } + QML_V4_END_INSTR(NewUrl, construct) + + QML_V4_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_V4_END_INSTR(Fetch, fetch) + + QML_V4_BEGIN_INSTR(TestV4Store, storetest) + { + Register &data = registers[instr->storetest.reg]; + testBindingResult(*testBindingSource, bindingLine, bindingColumn, context, + scope, data, instr->storetest.regType); + } + QML_V4_END_INSTR(TestV4Store, storetest) + + QML_V4_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_V4_END_INSTR(Store, store) + + QML_V4_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_V4_END_INSTR(Copy, copy) + + QML_V4_BEGIN_INSTR(Jump, jump) + if (instr->jump.reg == -1 || !registers[instr->jump.reg].getbool()) + code += instr->jump.count; + QML_V4_END_INSTR(Jump, jump) + + QML_V4_BEGIN_INSTR(BranchTrue, branchop) + if (registers[instr->branchop.reg].getbool()) + code += instr->branchop.offset; + QML_V4_END_INSTR(BranchTrue, branchop) + + QML_V4_BEGIN_INSTR(BranchFalse, branchop) + if (! registers[instr->branchop.reg].getbool()) + code += instr->branchop.offset; + QML_V4_END_INSTR(BranchFalse, branchop) + + QML_V4_BEGIN_INSTR(Branch, branchop) + code += instr->branchop.offset; + QML_V4_END_INSTR(Branch, branchop) + + QML_V4_BEGIN_INSTR(Block, blockop) + executedBlocks |= instr->blockop.block; + QML_V4_END_INSTR(Block, blockop) + + QML_V4_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_V4_END_INSTR(InitString, initstring) + + QML_V4_BEGIN_INSTR(CleanupRegister, cleanup) + registers[instr->cleanup.reg].cleanup(); + QML_V4_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..e67a3821d7 --- /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: + convertToBool(e->expr, src); + if (src != currentReg) { + i.move_reg_reg(currentReg, src); + gen(i); + } + break; + + case IR::OpNot: + convertToBool(e->expr, src); + 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/v4/qdeclarativev4compiler_p.h b/src/declarative/qml/v4/qdeclarativev4compiler_p.h new file mode 100644 index 0000000000..c10691dc87 --- /dev/null +++ b/src/declarative/qml/v4/qdeclarativev4compiler_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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_H +#define QDECLARATIVEV4COMPILER_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" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeFastProperties; +class QDeclarativeTypeNameCache; +class QDeclarativeV4CompilerPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeV4Compiler +{ +public: + QDeclarativeV4Compiler(); + ~QDeclarativeV4Compiler(); + + // Returns true if bindings were compiled + bool isValid() const; + + struct Expression + { + QDeclarativeParser::Object *component; + QDeclarativeParser::Object *context; + QDeclarativeParser::Property *property; + QDeclarativeParser::Variant expression; + QHash<QString, QDeclarativeParser::Object *> ids; + QDeclarativeTypeNameCache *importCache; + QDeclarativeImports imports; + }; + + // -1 on failure, otherwise the binding index to use + int compile(const Expression &, QDeclarativeEnginePrivate *); + + // Returns the compiled program + QByteArray program() const; + + static void dump(const QByteArray &); + static QDeclarativeFastProperties *fastPropertyAccessor(); + static void enableBindingsTest(bool); +private: + QDeclarativeV4CompilerPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#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..9c3bf914e0 --- /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_V4_INSTR(DUMP_INSTR_AT_STARTUP); + } +} dump_instr_at_startup; +#endif + +int Instr::size() const +{ +#define V4_RETURN_INSTR_SIZE(I, FMT) case I: return QML_V4_INSTR_SIZE(I, FMT); + switch (common.type) { + FOR_EACH_V4_INSTR(V4_RETURN_INSTR_SIZE) + } +#undef V4_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..67b152adea --- /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_V4_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_V4_INSTR_ALIGN_MASK (Q_ALIGNOF(Instr) - 1) +#else +# define QML_V4_INSTR_ALIGN_MASK (sizeof(void *) - 1) +#endif + +#define QML_V4_INSTR_ENUM(I, FMT) I, +#define QML_V4_INSTR_ADDR(I, FMT) &&op_##I, +#define QML_V4_INSTR_SIZE(I, FMT) ((sizeof(Instr::instr_##FMT) + QML_V4_INSTR_ALIGN_MASK) & ~QML_V4_INSTR_ALIGN_MASK) + +#ifdef QML_THREADED_INTERPRETER +# define QML_V4_BEGIN_INSTR(I,FMT) op_##I: +# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const Instr *) code; goto *instr->common.code; +# define QML_V4_INSTR_HEADER void *code; +#else +# define QML_V4_BEGIN_INSTR(I,FMT) case Instr::I: +# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const Instr *) code; break; +# define QML_V4_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_V4_INSTR(QML_V4_INSTR_ENUM) + }; + + struct instr_common { + QML_V4_INSTR_HEADER + quint8 type; + }; + + struct instr_id { + QML_V4_INSTR_HEADER + quint8 type; + quint16 column; + quint32 line; + }; + + struct instr_init { + QML_V4_INSTR_HEADER + quint8 type; + quint16 subscriptions; + quint16 identifiers; + }; + + struct instr_subscribeop { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + quint16 offset; + quint32 index; + }; + + struct instr_load { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + quint32 index; + }; + + struct instr_attached { + QML_V4_INSTR_HEADER + quint8 type; + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 id; + }; + + struct instr_store { + QML_V4_INSTR_HEADER + quint8 type; + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 index; + }; + + struct instr_storetest { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + qint32 regType; + }; + + struct instr_fetchAndSubscribe { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 exceptionId; + quint8 valueType; + quint16 subscription; + quint16 function; + }; + + struct instr_fetch{ + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 exceptionId; + quint8 valueType; + quint32 index; + }; + + struct instr_copy { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + qint8 src; + }; + + struct instr_construct { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + }; + + struct instr_real_value { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + qreal value; // XXX Makes the instruction 12 bytes + }; + + struct instr_int_value { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + int value; + }; + + struct instr_bool_value { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + bool value; + }; + + struct instr_string_value { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + quint16 length; + quint32 offset; + }; + + struct instr_binaryop { + QML_V4_INSTR_HEADER + quint8 type; + qint8 output; + qint8 left; + qint8 right; + }; + + struct instr_unaryop { + QML_V4_INSTR_HEADER + quint8 type; + qint8 output; + qint8 src; + }; + + struct instr_jump { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + quint32 count; + }; + + struct instr_find { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + qint8 src; + quint8 exceptionId; + quint16 name; + quint16 subscribeIndex; + }; + + struct instr_cleanup { + QML_V4_INSTR_HEADER + quint8 type; + qint8 reg; + }; + + struct instr_initstring { + QML_V4_INSTR_HEADER + quint8 type; + quint16 offset; + quint32 dataIdx; + }; + + struct instr_branchop { + QML_V4_INSTR_HEADER + quint8 type; + quint8 reg; + qint16 offset; + }; + + struct instr_blockop { + QML_V4_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 \ |