aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/v4
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/v4')
-rw-r--r--src/qml/qml/v4/qv4bindings.cpp1538
-rw-r--r--src/qml/qml/v4/qv4bindings_p.h153
-rw-r--r--src/qml/qml/v4/qv4compiler.cpp1399
-rw-r--r--src/qml/qml/v4/qv4compiler_p.h105
-rw-r--r--src/qml/qml/v4/qv4compiler_p_p.h245
-rw-r--r--src/qml/qml/v4/qv4instruction.cpp412
-rw-r--r--src/qml/qml/v4/qv4instruction_p.h432
-rw-r--r--src/qml/qml/v4/qv4ir.cpp882
-rw-r--r--src/qml/qml/v4/qv4ir_p.h604
-rw-r--r--src/qml/qml/v4/qv4irbuilder.cpp1303
-rw-r--r--src/qml/qml/v4/qv4irbuilder_p.h240
-rw-r--r--src/qml/qml/v4/qv4program_p.h122
-rw-r--r--src/qml/qml/v4/v4.pri15
13 files changed, 7450 insertions, 0 deletions
diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp
new file mode 100644
index 0000000000..c03292d74d
--- /dev/null
+++ b/src/qml/qml/v4/qv4bindings.cpp
@@ -0,0 +1,1538 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// #define REGISTER_CLEANUP_DEBUG
+
+#include "qv4bindings_p.h"
+#include "qv4program_p.h"
+#include "qv4compiler_p.h"
+#include "qv4compiler_p_p.h"
+
+#include <private/qqmlaccessors_p.h>
+#include <private/qqmlprofilerservice_p.h>
+#include <private/qqmlmetatype_p.h>
+#include <private/qqmltrace_p.h>
+
+#include <QtQml/qqmlinfo.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
+#include <math.h> // ::fmod
+
+QT_BEGIN_NAMESPACE
+
+using namespace QQmlJS;
+
+namespace {
+struct Register {
+ typedef QQmlRegisterType Type;
+
+ void setUndefined() { dataType = UndefinedType; }
+ void setNaN() { setqreal(qSNaN()); }
+ bool isUndefined() const { return dataType == UndefinedType; }
+
+ void setQObject(QObject *o) { qobjectValue = o; dataType = QObjectStarType; }
+ QObject *getQObject() const { return qobjectValue; }
+
+ void setqreal(qreal v) { qrealValue = v; dataType = QRealType; }
+ qreal getqreal() const { return qrealValue; }
+ qreal &getqrealref() { return qrealValue; }
+
+ void setint(int v) { intValue = v; dataType = IntType; }
+ int getint() const { return intValue; }
+ int &getintref() { return intValue; }
+
+ void setbool(bool v) { boolValue = v; dataType = BoolType; }
+ bool getbool() const { return boolValue; }
+ bool &getboolref() { return boolValue; }
+
+ 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; }
+
+ Type dataType; // Type of data
+ union {
+ QObject *qobjectValue;
+ qreal qrealValue;
+ int intValue;
+ bool boolValue;
+ void *data[sizeof(QVariant)];
+ qint64 q_for_alignment_1;
+ double q_for_alignment_2;
+ };
+
+ 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
+
+QV4Bindings::QV4Bindings(const char *programData,
+ QQmlContextData *context,
+ QQmlRefCount *ref)
+: subscriptions(0), program(0), dataRef(0), bindings(0)
+{
+ program = (QV4Program *)programData;
+ dataRef = ref;
+ if (dataRef) dataRef->addref();
+
+ if (program) {
+ subscriptions = new Subscription[program->subscriptions];
+ bindings = new Binding[program->bindings];
+
+ QQmlAbstractExpression::setContext(context);
+ }
+}
+
+QV4Bindings::~QV4Bindings()
+{
+ delete [] bindings;
+ delete [] subscriptions; subscriptions = 0;
+ if (dataRef) dataRef->release();
+}
+
+QQmlAbstractBinding *QV4Bindings::configBinding(int index, QObject *target,
+ QObject *scope, int property,
+ int line, int column)
+{
+ Binding *rv = bindings + index;
+
+ rv->index = index;
+ rv->property = property;
+ rv->target = target;
+ rv->scope = scope;
+ rv->line = line;
+ rv->column = column;
+ rv->parent = this;
+
+ addref(); // This is decremented in Binding::destroy()
+
+ return rv;
+}
+
+void QV4Bindings::Binding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
+{
+ if (enabled != e) {
+ enabled = e;
+
+ if (e) update(flags);
+ }
+}
+
+void QV4Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags)
+{
+ parent->run(this, flags);
+}
+
+void QV4Bindings::Binding::destroy()
+{
+ enabled = false;
+ removeFromObject();
+ clear();
+ removeError();
+ parent->release();
+}
+
+int QV4Bindings::Binding::propertyIndex() const
+{
+ return property;
+}
+
+QObject *QV4Bindings::Binding::object() const
+{
+ return target;
+}
+
+void QV4Bindings::Subscription::subscriptionCallback(QQmlNotifierEndpoint *e)
+{
+ Subscription *s = static_cast<Subscription *>(e);
+ s->bindings->subscriptionNotify(s->method);
+}
+
+void QV4Bindings::subscriptionNotify(int id)
+{
+ QV4Program::BindingReferenceList *list = program->signalTable(id);
+
+ for (quint32 ii = 0; ii < list->count; ++ii) {
+ QV4Program::BindingReference *bindingRef = list->bindings + ii;
+
+ Binding *binding = bindings + bindingRef->binding;
+
+ if (binding->executedBlocks & bindingRef->blockMask) {
+ run(binding, QQmlPropertyPrivate::DontRemoveBinding);
+ }
+ }
+}
+
+void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags)
+{
+ if (!binding->enabled)
+ return;
+
+ QQmlContextData *context = QQmlAbstractExpression::context();
+ if (!context || !context->isValid())
+ return;
+
+ QQmlTrace trace("V4 Binding Update");
+ trace.addDetail("URL", context->url);
+ trace.addDetail("Line", binding->line);
+ trace.addDetail("Column", binding->column);
+
+ QQmlBindingProfiler prof(context->urlString, binding->line, binding->column);
+
+ if (binding->updating) {
+ QString name;
+ if (binding->property & 0xFFFF0000) {
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
+
+ QQmlValueType *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) << tr("Binding loop detected for property \"%1\"").arg(name);
+ return;
+ }
+
+ binding->updating = true;
+ if (binding->property & 0xFFFF0000) {
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
+
+ QQmlValueType *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 QV4Bindings::unsubscribe(int subIndex)
+{
+ Subscription *sub = (subscriptions + subIndex);
+ sub->disconnect();
+}
+
+void QV4Bindings::subscribeId(QQmlContextData *p, int idIndex, int subIndex)
+{
+ unsubscribe(subIndex);
+
+ if (p->idValues[idIndex]) {
+ Subscription *sub = (subscriptions + subIndex);
+ sub->bindings = this;
+ sub->method = subIndex;
+ sub->connect(&p->idValues[idIndex].bindings);
+ }
+}
+
+void QV4Bindings::subscribe(QObject *o, int notifyIndex, int subIndex)
+{
+ Subscription *sub = (subscriptions + subIndex);
+ if (sub->isConnected(o, notifyIndex))
+ return;
+ sub->bindings = this;
+ sub->method = 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, QQmlContextData *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) {
+ // Preserve any valid percent-encoded octets supplied by the source
+ base.setEncodedUrl(var->toByteArray(), QUrl::TolerantMode);
+ } else if (vt == QVariant::String) {
+ base.setEncodedUrl(var->toString().toUtf8(), QUrl::TolerantMode);
+ } else {
+ if (ok) *ok = false;
+ return QUrl();
+ }
+ } else if (type == QMetaType::QString) {
+ base.setEncodedUrl(reg->getstringptr()->toUtf8(), QUrl::TolerantMode);
+ } else {
+ if (ok) *ok = false;
+ return QUrl();
+ }
+
+ if (!base.isEmpty() && base.isRelative())
+ return context->url.resolved(base);
+ else
+ return base;
+}
+
+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 == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
+ return QQmlMetaType::QQuickAnchorLineCompare(qtscript.constData(), v4.constData());
+ } 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,
+ QQmlContextData *context, QObject *scope,
+ const Register &result, int resultType)
+{
+ QQmlExpression expression(context->asQQmlContext(), 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 == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
+ v4value = QVariant(QQmlMetaType::QQuickAnchorLineMetaTypeId(), 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() << "QV4: 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,
+ QQmlContextData *context, QObject *scope)
+{
+ QQmlExpression expression(context->asQQmlContext(), scope, binding);
+ bool isUndefined = false;
+ QVariant value = expression.evaluate(&isUndefined);
+
+ if (!expression.hasError()) {
+ QByteArray qtscriptResult = testResultToString(value, isUndefined);
+ qWarning().nospace() << "QV4: 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, QQmlDelayedError *error,
+ QV4Program *program, QQmlContextData *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(QQmlEnginePrivate::get(context->engine)))
+ QQmlEnginePrivate::warning(context->engine, error->error);
+}
+
+const qreal QV4Bindings::D32 = 4294967296.0;
+
+qint32 QV4Bindings::toInt32(qreal n)
+{
+ if (qIsNaN(n) || qIsInf(n) || (n == 0))
+ return 0;
+
+ double sign = (n < 0) ? -1.0 : 1.0;
+ qreal 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 QV4Bindings::toUint32(qreal n)
+{
+ if (qIsNaN(n) || qIsInf(n) || (n == 0))
+ return 0;
+
+ double sign = (n < 0) ? -1.0 : 1.0;
+ qreal 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 **QV4Bindings::getDecodeInstrTable()
+{
+ static void **decode_instr;
+ if (!decode_instr) {
+ QV4Bindings *dummy = new QV4Bindings(0, 0, 0);
+ quint32 executedBlocks = 0;
+ dummy->run(0, executedBlocks, 0, 0, 0, 0,
+ QQmlPropertyPrivate::BypassInterceptor,
+ &decode_instr);
+ dummy->release();
+ }
+ return decode_instr;
+}
+#endif
+
+void QV4Bindings::run(int instrIndex, quint32 &executedBlocks,
+ QQmlContextData *context, QQmlDelayedError *error,
+ QObject *scope, QObject *output,
+ QQmlPropertyPrivate::WriteFlags storeFlags
+#ifdef QML_THREADED_INTERPRETER
+ ,void ***table
+#endif
+ )
+{
+#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;
+
+ const char *code = program->instructions();
+ code += instrIndex * QML_V4_INSTR_SIZE(Jump, jump);
+ const V4Instr *instr = reinterpret_cast<const V4Instr *>(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 &reg = 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;
+ Subscription *sub = 0;
+ if (subIdx != -1) {
+ sub = (subscriptions + subIdx);
+ sub->bindings = this;
+ sub->method = subIdx;
+ }
+ reg.init((Register::Type)instr->fetchAndSubscribe.valueType);
+ if (instr->fetchAndSubscribe.valueType >= FirstCleanupType)
+ MARK_REGISTER(instr->fetchAndSubscribe.reg);
+ QQmlAccessors *accessors = instr->fetchAndSubscribe.property.accessors;
+ accessors->read(object, instr->fetchAndSubscribe.property.accessorData,
+ reg.typeDataPtr());
+
+ if (accessors->notifier) {
+ QQmlNotifier *notifier = 0;
+ accessors->notifier(object, instr->fetchAndSubscribe.property.accessorData, &notifier);
+ if (notifier) sub->connect(notifier);
+ } else if (instr->fetchAndSubscribe.property.notifyIndex != -1) {
+ sub->connect(object, instr->fetchAndSubscribe.property.notifyIndex);
+ }
+ }
+ }
+ 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.
+ QJSValue 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.
+ QJSValue tmp(*src.getstringptr());
+ if (instr->unaryop.src == instr->unaryop.output) {
+ output.cleanupString();
+ MARK_CLEAN_REGISTER(instr->unaryop.output);
+ }
+ output.setint(tmp.toInt());
+ }
+ }
+ 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.
+ QJSValue 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(ConvertStringToUrl, unaryop)
+ {
+ const Register &src = registers[instr->unaryop.src];
+ Register &output = registers[instr->unaryop.output];
+ // ### NaN
+ if (src.isUndefined()) {
+ output.setUndefined();
+ } else {
+ const QString tmp(*src.getstringptr());
+ if (instr->unaryop.src == instr->unaryop.output) {
+ output.cleanupString();
+ MARK_CLEAN_REGISTER(instr->unaryop.output);
+ }
+ QUrl *urlPtr = output.geturlptr();
+ new (urlPtr) QUrl();
+ urlPtr->setEncodedUrl(tmp.toUtf8(), QUrl::TolerantMode);
+
+ URL_REGISTER(instr->unaryop.output);
+ }
+ }
+ QML_V4_END_INSTR(ConvertStringToUrl, unaryop)
+
+ QML_V4_BEGIN_INSTR(ConvertUrlToBool, unaryop)
+ {
+ const Register &src = registers[instr->unaryop.src];
+ Register &output = registers[instr->unaryop.output];
+ // ### NaN
+ if (src.isUndefined()) {
+ output.setUndefined();
+ } else {
+ const QUrl tmp(*src.geturlptr());
+ if (instr->unaryop.src == instr->unaryop.output) {
+ output.cleanupUrl();
+ MARK_CLEAN_REGISTER(instr->unaryop.output);
+ }
+ output.setbool(!tmp.isEmpty());
+ }
+ }
+ QML_V4_END_INSTR(ConvertUrlToBool, unaryop)
+
+ QML_V4_BEGIN_INSTR(ConvertUrlToString, unaryop)
+ {
+ const Register &src = registers[instr->unaryop.src];
+ Register &output = registers[instr->unaryop.output];
+ // ### NaN
+ if (src.isUndefined()) {
+ output.setUndefined();
+ } else {
+ const QUrl tmp(*src.geturlptr());
+ if (instr->unaryop.src == instr->unaryop.output) {
+ output.cleanupUrl();
+ MARK_CLEAN_REGISTER(instr->unaryop.output);
+ }
+ new (output.getstringptr()) QString(tmp.toString());
+ STRING_REGISTER(instr->unaryop.output);
+ }
+ }
+ QML_V4_END_INSTR(ConvertUrlToString, unaryop)
+
+ QML_V4_BEGIN_INSTR(ResolveUrl, unaryop)
+ {
+ const Register &src = registers[instr->unaryop.src];
+ Register &output = registers[instr->unaryop.output];
+ if (src.isUndefined()) {
+ output.setUndefined();
+ } else {
+ const QUrl tmp(*src.geturlptr());
+ if (instr->unaryop.src == instr->unaryop.output) {
+ *output.geturlptr() = context->resolvedUrl(tmp);
+ } else {
+ new (output.geturlptr()) QUrl(context->resolvedUrl(tmp));
+ URL_REGISTER(instr->unaryop.output);
+ }
+ }
+ }
+ QML_V4_END_INSTR(ResolveUrl, 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 qreal 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(LoadReal, real_value)
+ registers[instr->real_value.reg].setqreal(instr->real_value.value);
+ QML_V4_END_INSTR(LoadReal, real_value)
+
+ QML_V4_BEGIN_INSTR(LoadInt, int_value)
+ registers[instr->int_value.reg].setint(instr->int_value.value);
+ QML_V4_END_INSTR(LoadInt, int_value)
+
+ QML_V4_BEGIN_INSTR(LoadBool, bool_value)
+ registers[instr->bool_value.reg].setbool(instr->bool_value.value);
+ QML_V4_END_INSTR(LoadBool, bool_value)
+
+ QML_V4_BEGIN_INSTR(LoadString, 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(LoadString, 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 &reg = 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)
+
+ // XXX not applicable in v8
+ 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("QV4: 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/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h
new file mode 100644
index 0000000000..61d29a6f57
--- /dev/null
+++ b/src/qml/qml/v4/qv4bindings_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4BINDINGS_P_H
+#define QV4BINDINGS_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/qqmlexpression_p.h"
+#include "private/qqmlbinding_p.h"
+#include "private/qv4instruction_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+struct QV4Program;
+class QV4Bindings : public QQmlAbstractExpression,
+ public QQmlRefCount
+{
+ Q_DECLARE_TR_FUNCTIONS(QV4Bindings)
+public:
+ QV4Bindings(const char *program, QQmlContextData *context,
+ QQmlRefCount *);
+ virtual ~QV4Bindings();
+
+ QQmlAbstractBinding *configBinding(int index, QObject *target,
+ QObject *scope, int property,
+ int line, int column);
+
+#ifdef QML_THREADED_INTERPRETER
+ static void **getDecodeInstrTable();
+#endif
+
+private:
+ Q_DISABLE_COPY(QV4Bindings)
+
+ struct Binding : public QQmlAbstractBinding, public QQmlDelayedError {
+ Binding() : enabled(false), updating(0), property(0),
+ scope(0), target(0), executedBlocks(0), parent(0) {}
+
+ // Inherited from QQmlAbstractBinding
+ virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags);
+ virtual void update(QQmlPropertyPrivate::WriteFlags flags);
+ virtual void destroy();
+ virtual int propertyIndex() const;
+ virtual QObject *object() const;
+
+ int index:30;
+ bool enabled:1;
+ bool updating:1;
+ int property;
+ QObject *scope;
+ int line;
+ int column;
+ QObject *target;
+ quint32 executedBlocks;
+
+ QV4Bindings *parent;
+ };
+
+ class Subscription : public QQmlNotifierEndpoint
+ {
+ public:
+ Subscription() : bindings(0), method(-1) { callback = &subscriptionCallback; }
+ static void subscriptionCallback(QQmlNotifierEndpoint *e);
+ QV4Bindings *bindings;
+ int method;
+ };
+ friend class Subscription;
+
+ Subscription *subscriptions;
+
+ void subscriptionNotify(int);
+ void run(Binding *, QQmlPropertyPrivate::WriteFlags flags);
+
+ QV4Program *program;
+ QQmlRefCount *dataRef;
+ Binding *bindings;
+
+ void init();
+ void run(int instr, quint32 &executedBlocks, QQmlContextData *context,
+ QQmlDelayedError *error, QObject *scope, QObject *output,
+ QQmlPropertyPrivate::WriteFlags storeFlags
+#ifdef QML_THREADED_INTERPRETER
+ , void ***decode_instr = 0
+#endif
+ );
+
+
+ inline void unsubscribe(int subIndex);
+ inline void subscribeId(QQmlContextData *p, int idIndex, int subIndex);
+ inline void subscribe(QObject *o, int notifyIndex, int subIndex);
+
+ inline static qint32 toInt32(qreal n);
+ static const qreal D32;
+ static quint32 toUint32(qreal n);
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV4BINDINGS_P_H
+
diff --git a/src/qml/qml/v4/qv4compiler.cpp b/src/qml/qml/v4/qv4compiler.cpp
new file mode 100644
index 0000000000..d61fd580c7
--- /dev/null
+++ b/src/qml/qml/v4/qv4compiler.cpp
@@ -0,0 +1,1399 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4compiler_p.h"
+#include "qv4compiler_p_p.h"
+#include "qv4program_p.h"
+#include "qv4ir_p.h"
+#include "qv4irbuilder_p.h"
+
+#include <private/qqmlglobal_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmlaccessors_p.h>
+#include <private/qqmljsengine_p.h>
+
+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)
+
+static bool qmlBindingsTest = false;
+static bool qmlEnableV4 = true;
+
+using namespace QQmlJS;
+QV4CompilerPrivate::QV4CompilerPrivate()
+: _function(0) , _block(0) , _discarded(false)
+{
+}
+
+//
+// tracing
+//
+void QV4CompilerPrivate::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; !_discarded && 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) {
+ IR::Jump *jump = _function->pool->New<IR::Jump>();
+ jump->init(cj->iffalse);
+ block->statements.append(jump);
+ }
+ } else if (IR::Jump *j = terminator->asJump()) {
+ if (j->target == next) {
+ block->statements.resize(block->statements.size() - 1);
+ }
+ }
+ }
+
+ block->offset = bytecode.size();
+
+ if (bytecode.isEmpty()) {
+ if (qmlBindingsTest || bindingsDump()) {
+ Instr::BindingId id;
+ id.column = column;
+ 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::EnableV4Test test;
+ test.reg = 0;
+ test.offset = offset;
+ test.length = str.length();
+ gen(test);
+ }
+ }
+
+ bool usic = false;
+ int patchesCount = patches.count();
+ qSwap(usedSubscriptionIdsChanged, usic);
+
+ int blockopIndex = bytecode.size();
+ Instr::Block blockop;
+ blockop.block = currentBlockMask;
+ gen(blockop);
+
+ foreach (IR::Stmt *s, block->statements) {
+ if (! _discarded)
+ s->accept(this);
+ }
+
+ qSwap(usedSubscriptionIdsChanged, usic);
+
+ if (usic) {
+ if (currentBlockMask == 0x80000000) {
+ discard();
+ return;
+ }
+ currentBlockMask <<= 1;
+ } else if (! _discarded) {
+ 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
+
+
+ if (! _discarded) {
+ // back patching
+ foreach (const Patch &patch, patches) {
+ V4Instr &instr = bytecode[patch.offset];
+ int size = V4Instr::size(instructionType(&instr));
+ instr.branchop.offset = patch.block->offset - patch.offset - size;
+ }
+
+ patches.clear();
+ }
+}
+
+void QV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks)
+{
+ for (int i = 0; i < _function->basicBlocks.size(); ++i) {
+ IR::BasicBlock *block = _function->basicBlocks.at(i);
+
+ 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 QV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r)
+{
+ if (!e) {
+ discard();
+ } else {
+ qSwap(currentReg, r);
+ e->accept(this);
+ qSwap(currentReg, r);
+ }
+}
+
+//
+// expressions
+//
+void QV4CompilerPrivate::visitConst(IR::Const *e)
+{
+ switch (e->type) {
+ case IR::BoolType: {
+ Instr::LoadBool i;
+ i.reg = currentReg;
+ i.value = e->value;
+ gen(i);
+ } break;
+
+ case IR::IntType: {
+ Instr::LoadInt i;
+ i.reg = currentReg;
+ i.value = e->value;
+ gen(i);
+ } break;
+
+ case IR::RealType: {
+ Instr::LoadReal i;
+ i.reg = currentReg;
+ i.value = e->value;
+ gen(i);
+ } break;
+
+ default:
+ if (qmlVerboseCompiler())
+ qWarning() << Q_FUNC_INFO << "unexpected type";
+ discard();
+ }
+}
+
+void QV4CompilerPrivate::visitString(IR::String *e)
+{
+ registerLiteralString(currentReg, e->value);
+}
+
+void QV4CompilerPrivate::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::LoadRoot instr;
+ instr.reg = 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::LoadScope instr;
+ instr.reg = currentReg;
+ gen(instr);
+
+ _subscribeName << contextName();
+
+ } else if (e->storage == IR::Name::IdStorage) {
+
+ Instr::LoadId instr;
+ instr.reg = currentReg;
+ instr.index = e->idObject->idIndex;
+ gen(instr);
+
+ _subscribeName << QLatin1String("$$$ID_") + *e->id;
+
+ if (blockNeedsSubscription(_subscribeName)) {
+ Instr::SubscribeId sub;
+ sub.reg = currentReg;
+ sub.offset = subscriptionIndex(_subscribeName);
+ sub.index = instr.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::LoadAttached attached;
+ attached.output = currentReg;
+ attached.reg = currentReg;
+ attached.exceptionId = exceptionId(e->line, e->column);
+ if (e->declarativeType->attachedPropertiesId() == -1)
+ discard();
+ attached.id = e->declarativeType->attachedPropertiesId();
+ gen(attached);
+ } break;
+
+ case IR::Name::Property: {
+ _subscribeName << *e->id;
+
+ if (e->property->coreIndex == -1) {
+ QMetaProperty prop;
+ e->property->load(prop, QQmlEnginePrivate::get(engine));
+ }
+
+ const int propTy = e->property->propType;
+ QQmlRegisterType 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;
+ case QMetaType::QUrl:
+ regType = QUrlType;
+ break;
+
+ default:
+ if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
+ regType = PODValueType;
+ } else if (QQmlMetaType::isQObject(propTy)) {
+ regType = QObjectStarType;
+ } else {
+ if (qmlVerboseCompiler())
+ qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy);
+ discard(); // Unsupported type
+ return;
+ }
+
+ break;
+ } // switch
+
+ if (e->property->hasAccessors()) {
+ Instr::FetchAndSubscribe fetch;
+ fetch.reg = currentReg;
+ fetch.subscription = subscriptionIndex(_subscribeName);
+ fetch.exceptionId = exceptionId(e->line, e->column);
+ fetch.valueType = regType;
+ fetch.property = *e->property;
+ gen(fetch);
+ } else {
+ if (blockNeedsSubscription(_subscribeName) && e->property->notifyIndex != -1) {
+ Instr::Subscribe sub;
+ sub.reg = currentReg;
+ sub.offset = subscriptionIndex(_subscribeName);
+ sub.index = e->property->notifyIndex;
+ gen(sub);
+ }
+
+ Instr::Fetch fetch;
+ fetch.reg = currentReg;
+ fetch.index = e->property->coreIndex;
+ fetch.exceptionId = exceptionId(e->line, e->column);
+ fetch.valueType = regType;
+ gen(fetch);
+ }
+
+ } break;
+ } // switch
+}
+
+void QV4CompilerPrivate::visitTemp(IR::Temp *e)
+{
+ if (currentReg != e->index) {
+ Instr::Copy i;
+ i.reg = currentReg;
+ i.src = e->index;
+ gen(i);
+ }
+}
+
+void QV4CompilerPrivate::visitUnop(IR::Unop *e)
+{
+ 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) {
+ Instr::Copy i;
+ i.reg = currentReg;
+ i.src = src;
+ gen(i);
+ }
+ break;
+
+ case IR::OpNot: {
+ Instr::UnaryNot i;
+ convertToBool(e->expr, src);
+ i.output = currentReg;
+ i.src = src;
+ gen(i);
+ } break;
+
+ case IR::OpUMinus:
+ if (e->expr->type == IR::RealType) {
+ Instr::UnaryMinusReal i;
+ i.output = currentReg;
+ i.src = src;
+ gen(i);
+ } else if (e->expr->type == IR::IntType) {
+ convertToReal(e->expr, currentReg);
+ Instr::UnaryMinusReal i;
+ i.output = currentReg;
+ i.src = src;
+ gen(i);
+ } else {
+ discard();
+ }
+ break;
+
+ case IR::OpUPlus:
+ if (e->expr->type == IR::RealType) {
+ Instr::UnaryPlusReal i;
+ i.output = currentReg;
+ i.src = src;
+ gen(i);
+ } else if (e->expr->type == IR::IntType) {
+ convertToReal(e->expr, currentReg);
+ Instr::UnaryPlusReal i;
+ i.output = currentReg;
+ i.src = src;
+ gen(i);
+ } else {
+ discard();
+ }
+ break;
+
+ case IR::OpCompl:
+ // TODO
+ 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 QV4CompilerPrivate::convertToReal(IR::Expr *expr, int reg)
+{
+ if (expr->type == IR::RealType)
+ return;
+
+ switch (expr->type) {
+ case IR::BoolType: {
+ Instr::ConvertBoolToReal i;
+ i.output = i.src = reg;
+ gen(i);
+ } break;
+
+ case IR::IntType: {
+ Instr::ConvertIntToReal i;
+ i.output = i.src = reg;
+ gen(i);
+ } break;
+
+ case IR::RealType:
+ // nothing to do
+ return;
+
+ default:
+ discard();
+ break;
+ } // switch
+}
+
+void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg)
+{
+ if (expr->type == IR::IntType)
+ return;
+
+ switch (expr->type) {
+ case IR::BoolType: {
+ Instr::ConvertBoolToInt i;
+ i.output = i.src = reg;
+ gen(i);
+ } break;
+
+ case IR::IntType:
+ // nothing to do
+ return;
+
+ case IR::RealType: {
+ Instr::ConvertRealToInt i;
+ i.output = i.src = reg;
+ gen(i);
+ } break;
+
+ default:
+ discard();
+ break;
+ } // switch
+}
+
+void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg)
+{
+ if (expr->type == IR::BoolType)
+ return;
+
+ switch (expr->type) {
+ case IR::BoolType:
+ // nothing to do
+ break;
+
+ case IR::IntType: {
+ Instr::ConvertIntToBool i;
+ i.output = i.src = reg;
+ gen(i);
+ } break;
+
+ case IR::RealType: {
+ Instr::ConvertRealToBool i;
+ i.output = i.src = reg;
+ gen(i);
+ } return;
+
+ case IR::StringType: {
+ Instr::ConvertStringToBool i;
+ i.output = i.src = reg;
+ gen(i);
+ } return;
+
+ default:
+ discard();
+ break;
+ } // switch
+}
+
+quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e)
+{
+ switch (e->op) {
+ case IR::OpInvalid:
+ return V4Instr::Noop;
+
+ case IR::OpIfTrue:
+ case IR::OpNot:
+ case IR::OpUMinus:
+ case IR::OpUPlus:
+ case IR::OpCompl:
+ return V4Instr::Noop;
+
+ case IR::OpBitAnd:
+ return V4Instr::BitAndInt;
+
+ case IR::OpBitOr:
+ return V4Instr::BitOrInt;
+
+ case IR::OpBitXor:
+ return V4Instr::BitXorInt;
+
+ case IR::OpAdd:
+ if (e->type == IR::StringType)
+ return V4Instr::AddString;
+ return V4Instr::AddReal;
+
+ case IR::OpSub:
+ return V4Instr::SubReal;
+
+ case IR::OpMul:
+ return V4Instr::MulReal;
+
+ case IR::OpDiv:
+ return V4Instr::DivReal;
+
+ case IR::OpMod:
+ return V4Instr::ModReal;
+
+ case IR::OpLShift:
+ return V4Instr::LShiftInt;
+
+ case IR::OpRShift:
+ return V4Instr::RShiftInt;
+
+ case IR::OpURShift:
+ return V4Instr::URShiftInt;
+
+ case IR::OpGt:
+ if (e->left->type == IR::StringType)
+ return V4Instr::GtString;
+ return V4Instr::GtReal;
+
+ case IR::OpLt:
+ if (e->left->type == IR::StringType)
+ return V4Instr::LtString;
+ return V4Instr::LtReal;
+
+ case IR::OpGe:
+ if (e->left->type == IR::StringType)
+ return V4Instr::GeString;
+ return V4Instr::GeReal;
+
+ case IR::OpLe:
+ if (e->left->type == IR::StringType)
+ return V4Instr::LeString;
+ return V4Instr::LeReal;
+
+ case IR::OpEqual:
+ if (e->left->type == IR::StringType)
+ return V4Instr::EqualString;
+ return V4Instr::EqualReal;
+
+ case IR::OpNotEqual:
+ if (e->left->type == IR::StringType)
+ return V4Instr::NotEqualString;
+ return V4Instr::NotEqualReal;
+
+ case IR::OpStrictEqual:
+ if (e->left->type == IR::StringType)
+ return V4Instr::StrictEqualString;
+ return V4Instr::StrictEqualReal;
+
+ case IR::OpStrictNotEqual:
+ if (e->left->type == IR::StringType)
+ return V4Instr::StrictNotEqualString;
+ return V4Instr::StrictNotEqualReal;
+
+ case IR::OpAnd:
+ case IR::OpOr:
+ return V4Instr::Noop;
+
+ } // switch
+
+ return V4Instr::Noop;
+}
+
+void QV4CompilerPrivate::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 != V4Instr::Noop) {
+ V4Instr instr;
+ instr.binaryop.output = currentReg;
+ instr.binaryop.left = left;
+ instr.binaryop.right = right;
+ gen(static_cast<V4Instr::Type>(opcode), instr);
+ }
+}
+
+void QV4CompilerPrivate::visitCall(IR::Call *call)
+{
+ if (IR::Name *name = call->base->asName()) {
+ IR::Expr *arg = call->onlyArgument();
+ if (arg != 0 && arg->type == IR::RealType) {
+ traceExpression(arg, currentReg);
+
+ switch (name->builtin) {
+ case IR::NoBuiltinSymbol:
+ break;
+
+ case IR::MathSinBultinFunction: {
+ Instr::MathSinReal i;
+ i.output = i.src = currentReg;
+ gen(i);
+ } return;
+
+ case IR::MathCosBultinFunction: {
+ Instr::MathCosReal i;
+ i.output = i.src = currentReg;
+ gen(i);
+ } return;
+
+ case IR::MathRoundBultinFunction: {
+ Instr::MathRoundReal i;
+ i.output = i.src = currentReg;
+ gen(i);
+ } return;
+
+ case IR::MathFloorBultinFunction: {
+ Instr::MathFloorReal i;
+ i.output = i.src = currentReg;
+ gen(i);
+ } return;
+
+ case IR::MathPIBuiltinConstant:
+ break;
+ } // switch
+ }
+ }
+
+ if (qmlVerboseCompiler())
+ qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__;
+ discard();
+}
+
+
+//
+// statements
+//
+void QV4CompilerPrivate::visitExp(IR::Exp *s)
+{
+ traceExpression(s->expr, currentReg);
+}
+
+void QV4CompilerPrivate::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);
+
+ V4Instr::Type opcode = V4Instr::Noop;
+ IR::Type targetTy = s->target->type;
+ IR::Type sourceTy = s->source->type;
+
+ if (sourceTy == IR::UrlType) {
+ switch (targetTy) {
+ case IR::BoolType:
+ case IR::StringType:
+ // nothing to do. V4 will generate optimized
+ // url-to-xxx conversions.
+ break;
+ default: {
+ // generate a UrlToString conversion and fix
+ // the type of the source expression.
+ V4Instr conv;
+ conv.unaryop.output = V4Instr::ConvertUrlToString;
+ conv.unaryop.src = src;
+ gen(opcode, conv);
+
+ sourceTy = IR::StringType;
+ break;
+ }
+ } // switch
+ }
+
+ if (targetTy == IR::BoolType) {
+ switch (sourceTy) {
+ case IR::IntType: opcode = V4Instr::ConvertIntToBool; break;
+ case IR::RealType: opcode = V4Instr::ConvertRealToBool; break;
+ case IR::StringType: opcode = V4Instr::ConvertStringToBool; break;
+ case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break;
+ default: break;
+ } // switch
+ } else if (targetTy == IR::IntType) {
+ switch (sourceTy) {
+ case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break;
+ case IR::RealType: {
+ if (s->isMoveForReturn)
+ opcode = V4Instr::MathRoundReal;
+ else
+ opcode = V4Instr::ConvertRealToInt;
+ break;
+ }
+ case IR::StringType: opcode = V4Instr::ConvertStringToInt; break;
+ default: break;
+ } // switch
+ } else if (targetTy == IR::RealType) {
+ switch (sourceTy) {
+ case IR::BoolType: opcode = V4Instr::ConvertBoolToReal; break;
+ case IR::IntType: opcode = V4Instr::ConvertIntToReal; break;
+ case IR::StringType: opcode = V4Instr::ConvertStringToReal; break;
+ default: break;
+ } // switch
+ } else if (targetTy == IR::StringType) {
+ switch (sourceTy) {
+ case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break;
+ case IR::IntType: opcode = V4Instr::ConvertIntToString; break;
+ case IR::RealType: opcode = V4Instr::ConvertRealToString; break;
+ case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break;
+ default: break;
+ } // switch
+ } else if (targetTy == IR::UrlType) {
+ V4Instr convToString;
+ convToString.unaryop.output = dest;
+ convToString.unaryop.src = src;
+
+ // try to convert the source expression to a string.
+ switch (sourceTy) {
+ case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break;
+ case IR::IntType: gen(V4Instr::ConvertIntToString, convToString); sourceTy = IR::StringType; break;
+ case IR::RealType: gen(V4Instr::ConvertRealToString, convToString); sourceTy = IR::StringType; break;
+ default: break;
+ } // switch
+
+ if (sourceTy == IR::StringType)
+ opcode = V4Instr::ConvertStringToUrl;
+ }
+ if (opcode != V4Instr::Noop) {
+ V4Instr conv;
+ conv.unaryop.output = dest;
+ conv.unaryop.src = src;
+ gen(opcode, conv);
+
+ if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) {
+ V4Instr resolveUrl;
+ resolveUrl.unaryop.output = dest;
+ resolveUrl.unaryop.src = dest;
+ gen(V4Instr::ResolveUrl, resolveUrl);
+ }
+ } else {
+ discard();
+ }
+ } else {
+ traceExpression(s->source, dest);
+ }
+}
+
+void QV4CompilerPrivate::visitJump(IR::Jump *s)
+{
+ patches.append(Patch(s->target, bytecode.size()));
+
+ Instr::Branch i;
+ i.offset = 0; // ### backpatch
+ gen(i);
+}
+
+void QV4CompilerPrivate::visitCJump(IR::CJump *s)
+{
+ traceExpression(s->cond, currentReg);
+
+ patches.append(Patch(s->iftrue, bytecode.size()));
+
+ Instr::BranchTrue i;
+ i.reg = currentReg;
+ i.offset = 0; // ### backpatch
+ gen(i);
+}
+
+void QV4CompilerPrivate::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::TestV4Store test;
+ test.reg = storeReg;
+ switch (s->type) {
+ case IR::StringType:
+ test.regType = QMetaType::QString;
+ break;
+ case IR::UrlType:
+ test.regType = QMetaType::QUrl;
+ break;
+ case IR::SGAnchorLineType:
+ test.regType = QQmlMetaType::QQuickAnchorLineMetaTypeId();
+ break;
+ case IR::ObjectType:
+ test.regType = QMetaType::QObjectStar;
+ break;
+ case IR::BoolType:
+ test.regType = QMetaType::Bool;
+ break;
+ case IR::IntType:
+ test.regType = QMetaType::Int;
+ break;
+ case IR::RealType:
+ test.regType = QMetaType::QReal;
+ break;
+ default:
+ discard();
+ return;
+ }
+ gen(test);
+ }
+
+ Instr::Store store;
+ store.output = 0;
+ store.index = expression->property->index;
+ store.reg = storeReg;
+ store.exceptionId = exceptionId(s->line, s->column);
+ gen(store);
+}
+
+void QV4Compiler::dump(const QByteArray &programData)
+{
+ const QV4Program *program = (const QV4Program *)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 *end = start + programSize;
+ Bytecode bc;
+ bc.dump(start, end);
+}
+
+/*!
+Clear the state associated with attempting to compile a specific binding.
+This does not clear the global "committed binding" states.
+*/
+void QV4CompilerPrivate::resetInstanceState()
+{
+ data = committed.data;
+ exceptions = committed.exceptions;
+ usedSubscriptionIds.clear();
+ subscriptionIds = committed.subscriptionIds;
+ registeredStrings = committed.registeredStrings;
+ bytecode.clear();
+ patches.clear();
+ pool.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 QV4CompilerPrivate::commitCompile()
+{
+ int rv = committed.count();
+ committed.offsets << committed.bytecode.count();
+ committed.dependencies << usedSubscriptionIds;
+ committed.bytecode.append(bytecode.constData(), bytecode.size());
+ committed.data = data;
+ committed.exceptions = exceptions;
+ committed.subscriptionIds = subscriptionIds;
+ committed.registeredStrings = registeredStrings;
+ return rv;
+}
+
+bool QV4CompilerPrivate::compile(QQmlJS::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::Function thisFunction(&pool), *function = &thisFunction;
+
+ QV4IRBuilder irBuilder(expression, engine);
+ if (!irBuilder(function, 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 QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str)
+{
+ // ### string cleanup
+
+ QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
+ int offset = data.count();
+ data += strdata;
+
+ Instr::LoadString string;
+ string.reg = reg;
+ string.offset = offset;
+ string.length = str.length();
+ gen(string);
+
+ return reg;
+}
+
+// Returns an identifier offset
+int QV4CompilerPrivate::registerString(const QString &string)
+{
+ Q_ASSERT(!string.isEmpty());
+
+ QPair<int, int> *iter = registeredStrings.value(string);
+
+ if (!iter) {
+ 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[string];
+ *iter = qMakePair(registeredStrings.count(), rv);
+ }
+
+ Instr::InitString reg;
+ reg.offset = iter->first;
+ reg.dataIdx = iter->second;
+ gen(reg);
+ return reg.offset;
+}
+
+/*!
+Returns true if the current expression has not already subscribed to \a sub in currentBlockMask.
+*/
+bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub)
+{
+ QString str = sub.join(QLatin1String("."));
+
+ int *iter = subscriptionIds.value(str);
+ if (!iter)
+ return true;
+
+ quint32 *uiter = usedSubscriptionIds.value(*iter);
+ if (!uiter)
+ return true;
+ else
+ return !(*uiter & currentBlockMask);
+}
+
+int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub)
+{
+ QString str = sub.join(QLatin1String("."));
+ int *iter = subscriptionIds.value(str);
+ if (!iter) {
+ int count = subscriptionIds.count();
+ iter = &subscriptionIds[str];
+ *iter = count;
+ }
+ quint32 &u = usedSubscriptionIds[*iter];
+ if (!(u & currentBlockMask)) {
+ u |= currentBlockMask;
+ usedSubscriptionIdsChanged = true;
+ }
+ return *iter;
+}
+
+quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub)
+{
+ QString str = sub.join(QLatin1String("."));
+
+ int *iter = subscriptionIds.value(str);
+ Q_ASSERT(iter != 0);
+
+ quint32 *uiter = usedSubscriptionIds.value(*iter);
+ Q_ASSERT(uiter != 0);
+
+ return *uiter;
+}
+
+quint8 QV4CompilerPrivate::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 QV4CompilerPrivate::exceptionId(QQmlJS::AST::ExpressionNode *n)
+{
+ quint8 rv = 0xFF;
+ if (n && exceptions.count() < 0xFF) {
+ QQmlJS::AST::SourceLocation l = n->firstSourceLocation();
+ rv = exceptionId(l.startLine, l.startColumn);
+ }
+ return rv;
+}
+
+QV4Compiler::QV4Compiler()
+: d(new QV4CompilerPrivate)
+{
+ qmlBindingsTest |= qmlBindingsTestEnv();
+}
+
+QV4Compiler::~QV4Compiler()
+{
+ delete d; d = 0;
+}
+
+/*
+Returns true if any bindings were compiled.
+*/
+bool QV4Compiler::isValid() const
+{
+ return !d->committed.bytecode.isEmpty();
+}
+
+/*
+-1 on failure, otherwise the binding index to use.
+*/
+int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine)
+{
+ if (!expression.expression.asAST()) return false;
+
+ if (!qmlExperimental() && expression.property->isValueTypeSubProperty)
+ return -1;
+
+ if (qmlDisableOptimizer() || !qmlEnableV4)
+ return -1;
+
+ d->expression = &expression;
+ d->engine = engine;
+
+ if (d->compile(expression.expression.asAST())) {
+ return d->commitCompile();
+ } else {
+ return -1;
+ }
+}
+
+QByteArray QV4CompilerPrivate::buildSignalTable() const
+{
+ QHash<int, QList<QPair<int, quint32> > > table;
+
+ for (int ii = 0; ii < committed.count(); ++ii) {
+ const QQmlAssociationList<int, quint32> &deps = committed.dependencies.at(ii);
+ for (QQmlAssociationList<int, quint32>::const_iterator iter = deps.begin(); iter != deps.end(); ++iter)
+ table[iter->first].append(qMakePair(ii, iter->second));
+ }
+
+ 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 QV4CompilerPrivate::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 QV4Compiler::program() const
+{
+ QByteArray programData;
+
+ if (isValid()) {
+ QV4Program prog;
+ prog.bindings = d->committed.count();
+
+ Bytecode bc;
+ QV4CompilerPrivate::Instr::Jump jump;
+ jump.reg = -1;
+
+ for (int ii = 0; ii < d->committed.count(); ++ii) {
+ jump.count = d->committed.count() - ii - 1;
+ jump.count*= V4InstrMeta<V4Instr::Jump>::Size;
+ jump.count+= d->committed.offsets.at(ii);
+ bc.append(jump);
+ }
+
+
+ QByteArray bytecode;
+ bytecode.reserve(bc.size() + d->committed.bytecode.size());
+ bytecode.append(bc.constData(), bc.size());
+ bytecode.append(d->committed.bytecode.constData(), d->committed.bytecode.size());
+
+ 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(QV4Program) + bytecode.count();
+ size += prog.dataLength;
+
+ programData.resize(size);
+ memcpy(programData.data(), &prog, sizeof(QV4Program));
+ if (prog.dataLength)
+ memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(),
+ data.size());
+ memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(),
+ bytecode.count());
+ }
+
+ if (bindingsDump()) {
+ qWarning().nospace() << "Subscription slots:";
+
+ for (QQmlAssociationList<QString, int>::ConstIterator iter = d->committed.subscriptionIds.begin();
+ iter != d->committed.subscriptionIds.end();
+ ++iter) {
+ qWarning().nospace() << " " << iter->first << "\t-> " << iter->second;
+ }
+
+ QV4Compiler::dump(programData);
+ }
+
+ return programData;
+}
+
+void QV4Compiler::enableBindingsTest(bool e)
+{
+ if (e)
+ qmlBindingsTest = true;
+ else
+ qmlBindingsTest = qmlBindingsTestEnv();
+}
+
+void QV4Compiler::enableV4(bool e)
+{
+ qmlEnableV4 = e;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4compiler_p.h b/src/qml/qml/v4/qv4compiler_p.h
new file mode 100644
index 0000000000..a93248ad14
--- /dev/null
+++ b/src/qml/qml/v4/qv4compiler_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4COMPILER_P_H
+#define QV4COMPILER_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/qqmlexpression_p.h>
+#include <private/qqmlbinding_p.h>
+#include <private/qqmlcompiler_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTypeNameCache;
+class QV4CompilerPrivate;
+class Q_AUTOTEST_EXPORT QV4Compiler
+{
+public:
+ QV4Compiler();
+ ~QV4Compiler();
+
+ // Returns true if bindings were compiled
+ bool isValid() const;
+
+ struct Expression
+ {
+ Expression(const QQmlImports &imp) : imports(imp) {}
+ QQmlScript::Object *component;
+ QQmlScript::Object *context;
+ QQmlScript::Property *property;
+ QQmlScript::Variant expression;
+ QQmlCompilerTypes::IdList *ids;
+ QQmlTypeNameCache *importCache;
+ QQmlImports imports;
+ };
+
+ // -1 on failure, otherwise the binding index to use
+ int compile(const Expression &, QQmlEnginePrivate *);
+
+ // Returns the compiled program
+ QByteArray program() const;
+
+ static void dump(const QByteArray &);
+ static void enableBindingsTest(bool);
+ static void enableV4(bool);
+private:
+ QV4CompilerPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV4COMPILER_P_H
+
diff --git a/src/qml/qml/v4/qv4compiler_p_p.h b/src/qml/qml/v4/qv4compiler_p_p.h
new file mode 100644
index 0000000000..4b74a1d1c5
--- /dev/null
+++ b/src/qml/qml/v4/qv4compiler_p_p.h
@@ -0,0 +1,245 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4COMPILER_P_P_H
+#define QV4COMPILER_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 "qv4instruction_p.h"
+#include "qv4ir_p.h"
+#include <private/qqmlscript_p.h>
+#include <private/qqmlimport_p.h>
+#include <private/qqmlengine_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+template <typename _Key, typename _Value>
+class QQmlAssociationList
+{
+public:
+ typedef QVarLengthArray<QPair<_Key, _Value>, 8> Container;
+ typedef typename Container::const_iterator const_iterator;
+ typedef typename Container::const_iterator ConstIterator;
+
+ const_iterator begin() const { return _container.begin(); }
+ const_iterator end() const { return _container.end(); }
+ int count() const { return _container.count(); }
+ void clear() { _container.clear(); }
+
+ _Value *value(const _Key &key) {
+ for (int i = 0; i < _container.size(); ++i) {
+ QPair<_Key, _Value> &p = _container[i];
+ if (p.first == key)
+ return &p.second;
+ }
+ return 0;
+ }
+
+ _Value &operator[](const _Key &key) {
+ for (int i = 0; i < _container.size(); ++i) {
+ QPair<_Key, _Value> &p = _container[i];
+ if (p.first == key)
+ return p.second;
+ }
+ int index = _container.size();
+ _container.append(qMakePair(key, _Value()));
+ return _container[index].second;
+ }
+
+ void insert(const _Key &key, _Value &value) {
+ for (int i = 0; i < _container.size(); ++i) {
+ QPair<_Key, _Value> &p = _container[i];
+ if (p.first == key) {
+ p.second = value;
+ return;
+ }
+ }
+ _container.append(qMakePair(key, value));
+ }
+
+private:
+ Container _container;
+};
+
+class QV4CompilerPrivate: protected QQmlJS::IR::ExprVisitor,
+ protected QQmlJS::IR::StmtVisitor
+{
+public:
+ QV4CompilerPrivate();
+
+ void resetInstanceState();
+ int commitCompile();
+
+ const QV4Compiler::Expression *expression;
+ QQmlEnginePrivate *engine;
+
+ QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((quintptr)expression->context, 16); }
+
+ bool compile(QQmlJS::AST::Node *);
+
+ int registerLiteralString(quint8 reg, const QStringRef &);
+ int registerString(const QString &);
+ QQmlAssociationList<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(QQmlJS::AST::ExpressionNode *);
+ QVector<quint64> exceptions;
+
+ QQmlAssociationList<int, quint32> usedSubscriptionIds;
+
+ QQmlAssociationList<QString, int> subscriptionIds;
+ QQmlJS::Bytecode bytecode;
+
+ // back patching
+ struct Patch {
+ QQmlJS::IR::BasicBlock *block; // the basic block
+ int offset; // the index of the instruction to patch
+ Patch(QQmlJS::IR::BasicBlock *block = 0, int index = -1)
+ : block(block), offset(index) {}
+ };
+ QVector<Patch> patches;
+ QQmlPool pool;
+
+ // Committed binding data
+ struct {
+ QList<int> offsets;
+ QList<QQmlAssociationList<int, quint32> > dependencies;
+
+ //QQmlJS::Bytecode bytecode;
+ QByteArray bytecode;
+ QByteArray data;
+ QQmlAssociationList<QString, int> subscriptionIds;
+ QVector<quint64> exceptions;
+
+ QQmlAssociationList<QString, QPair<int, int> > registeredStrings;
+
+ int count() const { return offsets.count(); }
+ } committed;
+
+ QByteArray buildSignalTable() const;
+ QByteArray buildExceptionData() const;
+
+ void convertToReal(QQmlJS::IR::Expr *expr, int reg);
+ void convertToInt(QQmlJS::IR::Expr *expr, int reg);
+ void convertToBool(QQmlJS::IR::Expr *expr, int reg);
+ quint8 instructionOpcode(QQmlJS::IR::Binop *e);
+
+ struct Instr {
+#define QML_V4_INSTR_DATA_TYPEDEF(I, FMT) typedef QQmlJS::V4InstrData<QQmlJS::V4Instr::I> I;
+ FOR_EACH_V4_INSTR(QML_V4_INSTR_DATA_TYPEDEF)
+#undef QML_v4_INSTR_DATA_TYPEDEF
+ private:
+ Instr();
+ };
+
+protected:
+ //
+ // tracing
+ //
+ void trace(int line, int column);
+ void trace(QVector<QQmlJS::IR::BasicBlock *> *blocks);
+ void traceExpression(QQmlJS::IR::Expr *e, quint8 r);
+
+ template <int Instr>
+ inline void gen(const QQmlJS::V4InstrData<Instr> &i)
+ { bytecode.append(i); }
+ inline void gen(QQmlJS::V4Instr::Type type, QQmlJS::V4Instr &instr)
+ { bytecode.append(type, instr); }
+
+ inline QQmlJS::V4Instr::Type instructionType(const QQmlJS::V4Instr *i) const
+ { return bytecode.instructionType(i); }
+
+ //
+ // expressions
+ //
+ virtual void visitConst(QQmlJS::IR::Const *e);
+ virtual void visitString(QQmlJS::IR::String *e);
+ virtual void visitName(QQmlJS::IR::Name *e);
+ virtual void visitTemp(QQmlJS::IR::Temp *e);
+ virtual void visitUnop(QQmlJS::IR::Unop *e);
+ virtual void visitBinop(QQmlJS::IR::Binop *e);
+ virtual void visitCall(QQmlJS::IR::Call *e);
+
+ //
+ // statements
+ //
+ virtual void visitExp(QQmlJS::IR::Exp *s);
+ virtual void visitMove(QQmlJS::IR::Move *s);
+ virtual void visitJump(QQmlJS::IR::Jump *s);
+ virtual void visitCJump(QQmlJS::IR::CJump *s);
+ virtual void visitRet(QQmlJS::IR::Ret *s);
+
+private:
+ QStringList _subscribeName;
+ QQmlJS::IR::Function *_function;
+ QQmlJS::IR::BasicBlock *_block;
+ void discard() { _discarded = true; }
+ bool _discarded;
+ quint8 currentReg;
+
+ bool usedSubscriptionIdsChanged;
+ quint32 currentBlockMask;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV4COMPILER_P_P_H
+
diff --git a/src/qml/qml/v4/qv4instruction.cpp b/src/qml/qml/v4/qv4instruction.cpp
new file mode 100644
index 0000000000..08b2570747
--- /dev/null
+++ b/src/qml/qml/v4/qv4instruction.cpp
@@ -0,0 +1,412 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4instruction_p.h"
+#include "qv4bindings_p.h"
+
+#include <QtCore/qdebug.h>
+#include <private/qqmlglobal_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
+
+namespace QQmlJS {
+
+#ifdef DEBUG_INSTR_DUMP
+static struct DumpInstrAtStartup {
+ DumpInstrAtStartup() {
+ Bytecode bc;
+#define DUMP_INSTR_AT_STARTUP(I, FMT) { V4InstrData<V4Instr::I> i; bc.append(i); }
+ FOR_EACH_V4_INSTR(DUMP_INSTR_AT_STARTUP);
+#undef DUMP_INSTR_AT_STARTUP
+ const char *start = bc.constData();
+ const char *end = start + bc.size();
+ bc.dump(start, end);
+ }
+} dump_instr_at_startup;
+#endif
+
+int V4Instr::size(Type type)
+{
+#define V4_RETURN_INSTR_SIZE(I, FMT) case I: return QML_V4_INSTR_SIZE(I, FMT);
+ switch (type) {
+ FOR_EACH_V4_INSTR(V4_RETURN_INSTR_SIZE)
+ }
+#undef V4_RETURN_INSTR_SIZE
+ return 0;
+}
+
+void Bytecode::dump(const V4Instr *i, 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 (instructionType(i)) {
+ case V4Instr::Noop:
+ INSTR_DUMP << "\t" << "Noop";
+ break;
+ case V4Instr::BindingId:
+ INSTR_DUMP << i->id.line << ":" << i->id.column << ":";
+ break;
+ case V4Instr::Subscribe:
+ INSTR_DUMP << "\t" << "Subscribe" << "\t\t" << "Object_Reg(" << i->subscribeop.reg << ") Notify_Signal(" << i->subscribeop.index << ") -> Subscribe_Slot(" << i->subscribeop.offset << ")";
+ break;
+ case V4Instr::SubscribeId:
+ INSTR_DUMP << "\t" << "SubscribeId" << "\t\t" << "Id_Offset(" << i->subscribeop.index << ") -> Subscribe_Slot(" << i->subscribeop.offset << ")";
+ break;
+ case V4Instr::FetchAndSubscribe:
+ INSTR_DUMP << "\t" << "FetchAndSubscribe" << "\t" << "Object_Reg(" << i->fetchAndSubscribe.reg << ") Fast_Accessor(" << i->fetchAndSubscribe.property.accessors << ") -> Output_Reg(" << i->fetchAndSubscribe.reg << ") Subscription_Slot(" << i->fetchAndSubscribe.subscription << ")";
+ break;
+ case V4Instr::LoadId:
+ INSTR_DUMP << "\t" << "LoadId" << "\t\t\t" << "Id_Offset(" << i->load.index << ") -> Output_Reg(" << i->load.reg << ")";
+ break;
+ case V4Instr::LoadScope:
+ INSTR_DUMP << "\t" << "LoadScope" << "\t\t" << "-> Output_Reg(" << i->load.reg << ")";
+ break;
+ case V4Instr::LoadRoot:
+ INSTR_DUMP << "\t" << "LoadRoot" << "\t\t" << "-> Output_Reg(" << i->load.reg << ")";
+ break;
+ case V4Instr::LoadAttached:
+ INSTR_DUMP << "\t" << "LoadAttached" << "\t\t" << "Object_Reg(" << i->attached.reg << ") Attached_Index(" << i->attached.id << ") -> Output_Reg(" << i->attached.output << ")";
+ break;
+ case V4Instr::UnaryNot:
+ INSTR_DUMP << "\t" << "UnaryNot" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::UnaryMinusReal:
+ INSTR_DUMP << "\t" << "UnaryMinusReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::UnaryMinusInt:
+ INSTR_DUMP << "\t" << "UnaryMinusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::UnaryPlusReal:
+ INSTR_DUMP << "\t" << "UnaryPlusReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::UnaryPlusInt:
+ INSTR_DUMP << "\t" << "UnaryPlusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertBoolToInt:
+ INSTR_DUMP << "\t" << "ConvertBoolToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertBoolToReal:
+ INSTR_DUMP << "\t" << "ConvertBoolToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertBoolToString:
+ INSTR_DUMP << "\t" << "ConvertBoolToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertIntToBool:
+ INSTR_DUMP << "\t" << "ConvertIntToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertIntToReal:
+ INSTR_DUMP << "\t" << "ConvertIntToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertIntToString:
+ INSTR_DUMP << "\t" << "ConvertIntToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertRealToBool:
+ INSTR_DUMP << "\t" << "ConvertRealToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertRealToInt:
+ INSTR_DUMP << "\t" << "ConvertRealToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertRealToString:
+ INSTR_DUMP << "\t" << "ConvertRealToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertStringToBool:
+ INSTR_DUMP << "\t" << "ConvertStringToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertStringToInt:
+ INSTR_DUMP << "\t" << "ConvertStringToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertStringToReal:
+ INSTR_DUMP << "\t" << "ConvertStringToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertStringToUrl:
+ INSTR_DUMP << "\t" << "ConvertStringToUrl" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertUrlToBool:
+ INSTR_DUMP << "\t" << "ConvertUrlToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ConvertUrlToString:
+ INSTR_DUMP << "\t" << "ConvertUrlToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::ResolveUrl:
+ INSTR_DUMP << "\t" << "ResolveUrl" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::MathSinReal:
+ INSTR_DUMP << "\t" << "MathSinReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::MathCosReal:
+ INSTR_DUMP << "\t" << "MathCosReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::MathRoundReal:
+ INSTR_DUMP << "\t" << "MathRoundReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::MathFloorReal:
+ INSTR_DUMP << "\t" << "MathFloorReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::MathPIReal:
+ INSTR_DUMP << "\t" << "MathPIReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+ break;
+ case V4Instr::LoadReal:
+ INSTR_DUMP << "\t" << "LoadReal" << "\t\t" << "Constant(" << i->real_value.value << ") -> Output_Reg(" << i->real_value.reg << ")";
+ break;
+ case V4Instr::LoadInt:
+ INSTR_DUMP << "\t" << "LoadInt" << "\t\t\t" << "Constant(" << i->int_value.value << ") -> Output_Reg(" << i->int_value.reg << ")";
+ break;
+ case V4Instr::LoadBool:
+ INSTR_DUMP << "\t" << "LoadBool" << "\t\t" << "Constant(" << i->bool_value.value << ") -> Output_Reg(" << i->bool_value.reg << ")";
+ break;
+ case V4Instr::LoadString:
+ INSTR_DUMP << "\t" << "LoadString" << "\t\t" << "String_DataIndex(" << i->string_value.offset << ") String_Length(" << i->string_value.length << ") -> Output_Register(" << i->string_value.reg << ")";
+ break;
+ case V4Instr::EnableV4Test:
+ INSTR_DUMP << "\t" << "EnableV4Test" << "\t\t" << "String_DataIndex(" << i->string_value.offset << ") String_Length(" << i->string_value.length << ")";
+ break;
+ case V4Instr::TestV4Store:
+ INSTR_DUMP << "\t" << "TestV4Store" << "\t\t" << "Input_Reg(" << i->storetest.reg << ") Reg_Type(" << i->storetest.regType << ")";
+ break;
+ case V4Instr::BitAndInt:
+ INSTR_DUMP << "\t" << "BitAndInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::BitOrInt:
+ INSTR_DUMP << "\t" << "BitOrInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::BitXorInt:
+ INSTR_DUMP << "\t" << "BitXorInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::AddReal:
+ INSTR_DUMP << "\t" << "AddReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::AddString:
+ INSTR_DUMP << "\t" << "AddString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::SubReal:
+ INSTR_DUMP << "\t" << "SubReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::MulReal:
+ INSTR_DUMP << "\t" << "MulReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::DivReal:
+ INSTR_DUMP << "\t" << "DivReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::ModReal:
+ INSTR_DUMP << "\t" << "ModReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::LShiftInt:
+ INSTR_DUMP << "\t" << "LShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::RShiftInt:
+ INSTR_DUMP << "\t" << "RShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::URShiftInt:
+ INSTR_DUMP << "\t" << "URShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::GtReal:
+ INSTR_DUMP << "\t" << "GtReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::LtReal:
+ INSTR_DUMP << "\t" << "LtReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::GeReal:
+ INSTR_DUMP << "\t" << "GeReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::LeReal:
+ INSTR_DUMP << "\t" << "LeReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::EqualReal:
+ INSTR_DUMP << "\t" << "EqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::NotEqualReal:
+ INSTR_DUMP << "\t" << "NotEqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::StrictEqualReal:
+ INSTR_DUMP << "\t" << "StrictEqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::StrictNotEqualReal:
+ INSTR_DUMP << "\t" << "StrictNotEqualReal" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::GtString:
+ INSTR_DUMP << "\t" << "GtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::LtString:
+ INSTR_DUMP << "\t" << "LtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::GeString:
+ INSTR_DUMP << "\t" << "GeString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::LeString:
+ INSTR_DUMP << "\t" << "LeString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::EqualString:
+ INSTR_DUMP << "\t" << "EqualString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::NotEqualString:
+ INSTR_DUMP << "\t" << "NotEqualString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::StrictEqualString:
+ INSTR_DUMP << "\t" << "StrictEqualString" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::StrictNotEqualString:
+ INSTR_DUMP << "\t" << "StrictNotEqualString" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")";
+ break;
+ case V4Instr::NewString:
+ INSTR_DUMP << "\t" << "NewString" << "\t\t" << "Register(" << i->construct.reg << ")";
+ break;
+ case V4Instr::NewUrl:
+ INSTR_DUMP << "\t" << "NewUrl" << "\t\t\t" << "Register(" << i->construct.reg << ")";
+ break;
+ case V4Instr::CleanupRegister:
+ INSTR_DUMP << "\t" << "CleanupRegister" << "\t\t" << "Register(" << i->cleanup.reg << ")";
+ break;
+ case V4Instr::Fetch:
+ INSTR_DUMP << "\t" << "Fetch" << "\t\t\t" << "Object_Reg(" << i->fetch.reg << ") Property_Index(" << i->fetch.index << ") -> Output_Reg(" << i->fetch.reg << ")";
+ break;
+ case V4Instr::Store:
+ INSTR_DUMP << "\t" << "Store" << "\t\t\t" << "Input_Reg(" << i->store.reg << ") -> Object_Reg(" << i->store.output << ") Property_Index(" << i->store.index << ")";
+ break;
+ case V4Instr::Copy:
+ INSTR_DUMP << "\t" << "Copy" << "\t\t\t" << "Input_Reg(" << i->copy.src << ") -> Output_Reg(" << i->copy.reg << ")";
+ break;
+ case V4Instr::Jump:
+ if (i->jump.reg != -1) {
+ INSTR_DUMP << "\t" << "Jump" << "\t\t\t" << "Address(" << (address + size() + i->jump.count) << ") [if false == Input_Reg(" << i->jump.reg << ")]";
+ } else {
+ INSTR_DUMP << "\t" << "Jump" << "\t\t\t" << "Address(" << (address + size() + i->jump.count) << ")";
+ }
+ break;
+ case V4Instr::BranchFalse:
+ INSTR_DUMP << "\t" << "BranchFalse" << "\t\t" << "Address(" << (address + size() + i->branchop.offset) << ") [if false == Input_Reg(" << i->branchop.reg << ")]";
+ break;
+ case V4Instr::BranchTrue:
+ INSTR_DUMP << "\t" << "BranchTrue" << "\t\t" << "Address(" << (address + size() + i->branchop.offset) << ") [if true == Input_Reg(" << i->branchop.reg << ")]";
+ break;
+ case V4Instr::Branch:
+ INSTR_DUMP << "\t" << "Branch" << "\t\t\t" << "Address(" << (address + size() + i->branchop.offset) << ")";
+ break;
+ case V4Instr::InitString:
+ INSTR_DUMP << "\t" << "InitString" << "\t\t" << "String_DataIndex(" << i->initstring.dataIdx << ") -> String_Slot(" << i->initstring.offset << ")";
+ break;
+ case V4Instr::Block:
+ INSTR_DUMP << "\t" << "Block" << "\t\t\t" << "Mask(" << QByteArray::number(i->blockop.block, 16).constData() << ")";
+ break;
+ default:
+ INSTR_DUMP << "\t" << "Unknown";
+ break;
+ }
+}
+
+void Bytecode::dump(const char *start, const char *end) const
+{
+ const char *code = start;
+ while (code < end) {
+ const V4Instr *instr = reinterpret_cast<const V4Instr *>(code);
+ dump(instr, code - start);
+ code += V4Instr::size(instructionType(instr));
+ }
+}
+
+Bytecode::Bytecode()
+{
+#ifdef QML_THREADED_INTERPRETER
+ decodeInstr = QV4Bindings::getDecodeInstrTable();
+#endif
+}
+
+V4Instr::Type Bytecode::instructionType(const V4Instr *instr) const
+{
+#ifdef QML_THREADED_INTERPRETER
+ void *code = instr->common.code;
+
+# define CHECK_V4_INSTR_CODE(I, FMT) \
+ if (decodeInstr[static_cast<int>(V4Instr::I)] == code) \
+ return V4Instr::I;
+
+ FOR_EACH_V4_INSTR(CHECK_V4_INSTR_CODE)
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid instruction address");
+ return static_cast<V4Instr::Type>(0);
+# undef CHECK_V4_INSTR_CODE
+#else
+ return static_cast<V4Instr::Type>(instr->common.type);
+#endif
+
+}
+
+void Bytecode::append(V4Instr::Type type, V4Instr &instr)
+{
+#ifdef QML_THREADED_INTERPRETER
+ instr.common.code = decodeInstr[static_cast<int>(type)];
+#else
+ instr.common.type = type;
+#endif
+ d.append(reinterpret_cast<const char *>(&instr), V4Instr::size(type));
+}
+
+int Bytecode::remove(int offset)
+{
+ const V4Instr *instr = reinterpret_cast<const V4Instr *>(d.begin() + offset);
+ const int instrSize = V4Instr::size(instructionType(instr));
+ d.remove(offset, instrSize);
+ return instrSize;
+}
+
+const V4Instr &Bytecode::operator[](int offset) const
+{
+ return *(reinterpret_cast<const V4Instr *>(d.begin() + offset));
+}
+
+V4Instr &Bytecode::operator[](int offset)
+{
+ return *(reinterpret_cast<V4Instr *>(d.begin() + offset));
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4instruction_p.h b/src/qml/qml/v4/qv4instruction_p.h
new file mode 100644
index 0000000000..8150eedf54
--- /dev/null
+++ b/src/qml/qml/v4/qv4instruction_p.h
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4INSTRUCTION_P_H
+#define QV4INSTRUCTION_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>
+#include <QtCore/qvarlengtharray.h>
+
+#include <private/qqmlpropertycache_p.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(ConvertStringToUrl, unaryop) \
+ F(ConvertUrlToBool, unaryop) \
+ F(ConvertUrlToString, unaryop) \
+ F(ResolveUrl, unaryop) \
+ F(MathSinReal, unaryop) \
+ F(MathCosReal, unaryop) \
+ F(MathRoundReal, unaryop) \
+ F(MathFloorReal, unaryop) \
+ F(MathPIReal, unaryop) \
+ F(LoadReal, real_value) \
+ F(LoadInt, int_value) \
+ F(LoadBool, bool_value) \
+ F(LoadString, 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(V4Instr) - 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(V4Instr::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 V4Instr *) code; goto *instr->common.code;
+# define QML_V4_INSTR_HEADER void *code;
+#else
+# define QML_V4_BEGIN_INSTR(I,FMT) case V4Instr::I:
+# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const V4Instr *) code; break;
+# define QML_V4_INSTR_HEADER quint8 type;
+#endif
+
+class QObject;
+class QQmlNotifier;
+
+namespace QQmlJS {
+
+union V4Instr {
+ enum Type {
+ FOR_EACH_V4_INSTR(QML_V4_INSTR_ENUM)
+ };
+
+ static int size(Type type);
+
+ struct instr_common {
+ QML_V4_INSTR_HEADER
+ };
+
+ struct instr_id {
+ QML_V4_INSTR_HEADER
+ quint16 column;
+ quint32 line;
+ };
+
+ struct instr_init {
+ QML_V4_INSTR_HEADER
+ quint16 subscriptions;
+ quint16 identifiers;
+ };
+
+ struct instr_subscribeop {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ quint16 offset;
+ quint32 index;
+ };
+
+ struct instr_load {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ quint32 index;
+ };
+
+ struct instr_attached {
+ QML_V4_INSTR_HEADER
+ qint8 output;
+ qint8 reg;
+ quint8 exceptionId;
+ quint32 id;
+ };
+
+ struct instr_store {
+ QML_V4_INSTR_HEADER
+ qint8 output;
+ qint8 reg;
+ quint8 exceptionId;
+ quint32 index;
+ };
+
+ struct instr_storetest {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ qint32 regType;
+ };
+
+ struct instr_fetchAndSubscribe {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ quint8 exceptionId;
+ quint8 valueType;
+ quint16 subscription;
+ QQmlPropertyRawData property;
+ };
+
+ struct instr_fetch{
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ quint8 exceptionId;
+ quint8 valueType;
+ quint32 index;
+ };
+
+ struct instr_copy {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ qint8 src;
+ };
+
+ struct instr_construct {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ };
+
+ struct instr_real_value {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ qreal value; // XXX Makes the instruction 12 bytes
+ };
+
+ struct instr_int_value {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ int value;
+ };
+
+ struct instr_bool_value {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ bool value;
+ };
+
+ struct instr_string_value {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ quint16 length;
+ quint32 offset;
+ };
+
+ struct instr_binaryop {
+ QML_V4_INSTR_HEADER
+ qint8 output;
+ qint8 left;
+ qint8 right;
+ };
+
+ struct instr_unaryop {
+ QML_V4_INSTR_HEADER
+ qint8 output;
+ qint8 src;
+ };
+
+ struct instr_jump {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ quint32 count;
+ };
+
+ struct instr_find {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ qint8 src;
+ quint8 exceptionId;
+ quint16 name;
+ quint16 subscribeIndex;
+ };
+
+ struct instr_cleanup {
+ QML_V4_INSTR_HEADER
+ qint8 reg;
+ };
+
+ struct instr_initstring {
+ QML_V4_INSTR_HEADER
+ quint16 offset;
+ quint32 dataIdx;
+ };
+
+ struct instr_branchop {
+ QML_V4_INSTR_HEADER
+ quint8 reg;
+ qint16 offset;
+ };
+
+ struct instr_blockop {
+ QML_V4_INSTR_HEADER
+ 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;
+};
+
+template<int N>
+struct V4InstrMeta {
+};
+
+#define QML_V4_INSTR_META_TEMPLATE(I, FMT) \
+ template<> struct V4InstrMeta<(int)V4Instr::I> { \
+ enum { Size = QML_V4_INSTR_SIZE(I, FMT) }; \
+ typedef V4Instr::instr_##FMT DataType; \
+ static const DataType &data(const V4Instr &instr) { return instr.FMT; } \
+ static void setData(V4Instr &instr, const DataType &v) { instr.FMT = v; } \
+ };
+FOR_EACH_V4_INSTR(QML_V4_INSTR_META_TEMPLATE);
+#undef QML_V4_INSTR_META_TEMPLATE
+
+template<int Instr>
+class V4InstrData : public V4InstrMeta<Instr>::DataType
+{
+};
+
+class Bytecode
+{
+ Q_DISABLE_COPY(Bytecode)
+
+public:
+ Bytecode();
+
+ 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(); }
+ V4Instr::Type instructionType(const V4Instr *instr) const;
+
+ template <int Instr>
+ void append(const V4InstrData<Instr> &data)
+ {
+ V4Instr genericInstr;
+ V4InstrMeta<Instr>::setData(genericInstr, data);
+ return append(static_cast<V4Instr::Type>(Instr), genericInstr);
+ }
+ void append(V4Instr::Type type, V4Instr &instr);
+
+ int remove(int index);
+
+ const V4Instr &operator[](int offset) const;
+ V4Instr &operator[](int offset);
+
+ void dump(const char *start, const char *end) const;
+
+private:
+ void dump(const V4Instr *instr, int = -1) const;
+
+ QVarLengthArray<char, 4 * 1024> d;
+#ifdef QML_THREADED_INTERPRETER
+ void **decodeInstr;
+#endif
+};
+
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV4INSTRUCTION_P_H
+
diff --git a/src/qml/qml/v4/qv4ir.cpp b/src/qml/qml/v4/qv4ir.cpp
new file mode 100644
index 0000000000..be822145a4
--- /dev/null
+++ b/src/qml/qml/v4/qv4ir.cpp
@@ -0,0 +1,882 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4ir_p.h"
+
+#include <QtCore/qtextstream.h>
+#include <QtCore/qdebug.h>
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+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 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";
+ }
+}
+
+inline bool isNumberType(IR::Type ty)
+{
+ return ty >= IR::FirstNumberType;
+}
+
+inline bool isStringType(IR::Type ty)
+{
+ return ty == IR::StringType || ty == IR::UrlType;
+}
+
+IR::Type maxType(IR::Type left, IR::Type right)
+{
+ if (isStringType(left) && isStringType(right)) {
+ // String promotions (url to string) are more specific than
+ // identity conversions (AKA left == right). That's because
+ // we want to ensure we convert urls to strings in binary
+ // expressions.
+ return IR::StringType;
+ } else if (left == right)
+ return left;
+ else if (isNumberType(left) && isNumberType(right))
+ return qMax(left, right);
+ else if ((isNumberType(left) && isStringType(right)) ||
+ (isNumberType(right) && isStringType(left)))
+ 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 QStringRef &s)
+{
+ QString r;
+ for (int i = 0; i < s.length(); ++i) {
+ const QChar ch = s.at(i);
+ 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;
+}
+
+void Name::init(Name *base, Type type, const QString *id, Symbol symbol, quint32 line, quint32 column)
+{
+ this->type = type;
+ this->base = base;
+ this->id = id;
+ this->symbol = symbol;
+ this->ptr = 0;
+ this->property = 0;
+ this->storage = MemberStorage;
+ this->builtin = NoBuiltinSymbol;
+ this->line = line;
+ this->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;
+ this->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 (ExprList *it = args; it; it = it->next) {
+ if (it != args)
+ out << ", ";
+ it->expr->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);
+}
+
+QString *Function::newString(const QString &text)
+{
+ return pool->NewString(text);
+}
+
+BasicBlock *Function::newBasicBlock()
+{
+ const int index = basicBlocks.size();
+ return i(new BasicBlock(this, index));
+}
+
+void Function::dump(QTextStream &out)
+{
+ out << "function () {" << endl;
+ foreach (BasicBlock *bb, basicBlocks) {
+ bb->dump(out);
+ }
+ out << '}' << endl;
+}
+
+Temp *BasicBlock::TEMP(Type type, int index)
+{
+ Temp *e = function->pool->New<Temp>();
+ e->init(type, index);
+ return e;
+}
+
+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)
+{
+ Const *e = function->pool->New<Const>();
+ e->init(type, value);
+ return e;
+}
+
+Expr *BasicBlock::STRING(const QStringRef &value)
+{
+ String *e = function->pool->New<String>();
+ e->init(value);
+ return e;
+}
+
+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)
+{
+ Name *e = function->pool->New<Name>();
+ e->init(base, InvalidType,
+ function->newString(id),
+ Name::Unbound, line, column);
+ return e;
+}
+
+Name *BasicBlock::SYMBOL(Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, Name::Storage storage,
+ quint32 line, quint32 column)
+{
+ Name *name = SYMBOL(/*base = */ 0, type, id, meta, property, line, column);
+ name->storage = storage;
+ return name;
+}
+
+Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, Name::Storage storage,
+ quint32 line, quint32 column)
+{
+ Name *name = function->pool->New<Name>();
+ name->init(base, type, function->newString(id),
+ Name::Property, line, column);
+ name->meta = meta;
+ name->property = property;
+ name->storage = storage;
+ return name;
+}
+
+Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property,
+ quint32 line, quint32 column)
+{
+ Name *name = function->pool->New<Name>();
+ name->init(base, type, function->newString(id),
+ Name::Property, line, column);
+ name->meta = meta;
+ name->property = property;
+ return name;
+}
+
+Name *BasicBlock::ID_OBJECT(const QString &id, const QQmlScript::Object *object, quint32 line, quint32 column)
+{
+ Name *name = function->pool->New<Name>();
+ name->init(/*base = */ 0, IR::ObjectType,
+ function->newString(id),
+ Name::IdObject, line, column);
+ name->idObject = object;
+ name->property = 0;
+ name->storage = Name::IdStorage;
+ return name;
+}
+
+Name *BasicBlock::ATTACH_TYPE(const QString &id, const QQmlType *attachType, Name::Storage storage,
+ quint32 line, quint32 column)
+{
+ Name *name = function->pool->New<Name>();
+ name->init(/*base = */ 0, IR::AttachType,
+ function->newString(id),
+ Name::AttachType, line, column);
+ name->declarativeType = attachType;
+ name->storage = storage;
+ return name;
+}
+
+
+Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
+{
+ Unop *e = function->pool->New<Unop>();
+ e->init(op, expr);
+ return e;
+}
+
+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;
+ }
+ }
+ }
+ }
+
+ Binop *e = function->pool->New<Binop>();
+ e->init(op, left, right);
+ return e;
+}
+
+Expr *BasicBlock::CALL(Expr *base, ExprList *args)
+{
+ Call *e = function->pool->New<Call>();
+ e->init(base, args);
+ return e;
+}
+
+Stmt *BasicBlock::EXP(Expr *expr)
+{
+ Exp *s = function->pool->New<Exp>();
+ s->init(expr);
+ statements.append(s);
+ return s;
+}
+
+Stmt *BasicBlock::MOVE(Expr *target, Expr *source, bool isMoveForReturn)
+{
+ Move *s = function->pool->New<Move>();
+ s->init(target, source, isMoveForReturn);
+ statements.append(s);
+ return s;
+}
+
+Stmt *BasicBlock::JUMP(BasicBlock *target)
+{
+ if (isTerminated())
+ return 0;
+
+ Jump *s = function->pool->New<Jump>();
+ s->init(target);
+ statements.append(s);
+ return s;
+}
+
+Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
+{
+ if (isTerminated())
+ return 0;
+
+ CJump *s = function->pool->New<CJump>();
+ s->init(cond, iftrue, iffalse);
+ statements.append(s);
+ return s;
+}
+
+Stmt *BasicBlock::RET(Expr *expr, Type type, quint32 line, quint32 column)
+{
+ if (isTerminated())
+ return 0;
+
+ Ret *s = function->pool->New<Ret>();
+ s->init(expr, type, line, column);
+ statements.append(s);
+ return s;
+}
+
+void BasicBlock::dump(QTextStream &out)
+{
+ out << 'L' << this << ':' << endl;
+ foreach (Stmt *s, statements) {
+ out << '\t';
+ s->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(QQmlJS::IR::Expr *e)
+{
+ inc();
+
+ e->accept(this);
+
+ dec();
+}
+
+void IRDump::basicblock(QQmlJS::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(QQmlJS::IR::Stmt *s)
+{
+ inc();
+
+ s->accept(this);
+
+ dec();
+}
+
+void IRDump::function(QQmlJS::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(QQmlJS::IR::Const *e)
+{
+ qWarning().nospace() << indent() << "Const:Expr { type: " << typeName(e->type) << ", value: " << e->value << "}";
+}
+
+void IRDump::visitString(QQmlJS::IR::String *e)
+{
+ qWarning().nospace() << indent() << "String:Expr { type: " << typeName(e->type) << ", value: " << e->value << "}";
+}
+
+static void namedumprecur(QQmlJS::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(QQmlJS::IR::Name *e)
+{
+ qWarning().nospace() << indent() << "Name:Expr {";
+ namedumprecur(e, indent());
+ qWarning().nospace() << indent() << "}";
+}
+
+void IRDump::visitTemp(QQmlJS::IR::Temp *e)
+{
+ qWarning().nospace() << indent() << "Temp:Expr { type: " << typeName(e->type) << ", index: " << e->index << " }";
+}
+
+void IRDump::visitUnop(QQmlJS::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(QQmlJS::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(QQmlJS::IR::Call *e)
+{
+ Q_UNUSED(e);
+ qWarning().nospace() << indent() << "Exp::Call { }";
+}
+
+void IRDump::visitExp(QQmlJS::IR::Exp *s)
+{
+ qWarning().nospace() << indent() << "Exp:Stmt {";
+ expression(s->expr);
+ qWarning().nospace() << indent() << "}";
+}
+
+void IRDump::visitMove(QQmlJS::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(QQmlJS::IR::Jump *s)
+{
+ qWarning().nospace() << indent() << "Jump:Stmt { BasicBlock(" << s->target << ") }";
+}
+
+void IRDump::visitCJump(QQmlJS::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(QQmlJS::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 QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4ir_p.h b/src/qml/qml/v4/qv4ir_p.h
new file mode 100644
index 0000000000..48a08adf9f
--- /dev/null
+++ b/src/qml/qml/v4/qv4ir_p.h
@@ -0,0 +1,604 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4IR_P_H
+#define QV4IR_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/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
+#include <private/qqmlscript_p.h>
+#include <private/qqmlimport_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qv4compiler_p.h>
+
+#include <private/qqmlpool_p.h>
+#include <QtCore/qvarlengtharray.h>
+
+// #define DEBUG_IR_STRUCTURE
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QTextStream;
+class QQmlType;
+
+namespace QQmlJS {
+
+namespace IR {
+
+struct BasicBlock;
+struct Function;
+
+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,
+ 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: QQmlPool::POD {
+ Type type;
+
+ Expr(): type(InvalidType) {}
+ 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 ExprList: QQmlPool::POD {
+ Expr *expr;
+ ExprList *next;
+
+ void init(Expr *expr, ExprList *next = 0)
+ {
+ this->expr = expr;
+ this->next = next;
+ }
+};
+
+struct Const: Expr {
+ double value;
+
+ void init(Type type, double value)
+ {
+ this->type = type;
+ this->value = value;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitConst(this); }
+ virtual Const *asConst() { return this; }
+
+ virtual void dump(QTextStream &out);
+};
+
+struct String: Expr {
+ QStringRef value;
+
+ void init(const QStringRef &value)
+ {
+ this->type = StringType;
+ this->value = value;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitString(this); }
+ virtual String *asString() { return this; }
+
+ virtual void dump(QTextStream &out);
+ static QString escape(const QStringRef &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;
+ const QString *id;
+ Symbol symbol;
+ union {
+ void *ptr;
+ const QMetaObject *meta;
+ const QQmlType *declarativeType;
+ const QQmlScript::Object *idObject;
+ };
+ QQmlPropertyData *property;
+ Storage storage;
+ BuiltinSymbol builtin;
+ quint32 line;
+ quint32 column;
+
+ void init(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;
+
+ void init(Type type, int index)
+ {
+ this->type = type;
+ this->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;
+
+ void init(AluOp op, Expr *expr)
+ {
+ this->type = this->typeForOp(op, expr);
+ this->op = op;
+ this->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;
+
+ void init(AluOp op, Expr *left, Expr *right)
+ {
+ this->type = typeForOp(op, left, right);
+ this->op = op;
+ this->left = left;
+ this->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;
+ ExprList *args;
+
+ void init(Expr *base, ExprList *args)
+ {
+ this->type = typeForFunction(base);
+ this->base = base;
+ this->args = args;
+ }
+
+ Expr *onlyArgument() const {
+ if (args && ! args->next)
+ return args->expr;
+ return 0;
+ }
+
+ 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: QQmlPool::POD {
+ 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;
+
+ void init(Expr *expr)
+ {
+ this->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;
+
+ void init(Expr *target, Expr *source, bool isMoveForReturn)
+ {
+ this->target = target;
+ this->source = source;
+ this->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;
+
+ void init(BasicBlock *target)
+ {
+ this->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;
+
+ void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
+ {
+ this->cond = cond;
+ this->iftrue = iftrue;
+ this->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;
+
+ void init(Expr *expr, Type type, quint32 line, quint32 column)
+ {
+ this->expr = expr;
+ this->type = type;
+ this->line = line;
+ this->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 {
+ QQmlPool *pool;
+ QVarLengthArray<BasicBlock *, 8> basicBlocks;
+ int tempCount;
+
+ Function(QQmlPool *pool)
+ : pool(pool), tempCount(0) {}
+
+ ~Function();
+
+ BasicBlock *newBasicBlock();
+ QString *newString(const QString &text);
+
+ inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; }
+
+ virtual void dump(QTextStream &out);
+};
+
+struct BasicBlock {
+ Function *function;
+ int index;
+ int offset;
+ QVarLengthArray<Stmt *, 32> statements;
+
+ BasicBlock(Function *function, int index): function(function), index(index), offset(-1) {}
+ ~BasicBlock() {}
+
+ 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.at(statements.size() - 1)->asTerminator() != 0)
+ return statements.at(statements.size() - 1);
+ 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 QStringRef &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, QQmlPropertyData *property, Name::Storage storage, quint32 line, quint32 column);
+ Name *SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, quint32 line, quint32 column);
+ Name *SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QQmlPropertyData *property, Name::Storage storage, quint32 line, quint32 column);
+ Name *ID_OBJECT(const QString &id, const QQmlScript::Object *object, quint32 line, quint32 column);
+ Name *ATTACH_TYPE(const QString &id, const QQmlType *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, ExprList *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);
+};
+
+#ifdef DEBUG_IR_STRUCTURE
+struct IRDump : public ExprVisitor,
+ public StmtVisitor
+{
+public:
+ IRDump();
+
+ void expression(QQmlJS::IR::Expr *);
+ void basicblock(QQmlJS::IR::BasicBlock *);
+ void statement(QQmlJS::IR::Stmt *);
+ void function(QQmlJS::IR::Function *);
+protected:
+
+ const char *indent();
+
+ //
+ // expressions
+ //
+ virtual void visitConst(QQmlJS::IR::Const *e);
+ virtual void visitString(QQmlJS::IR::String *e);
+ virtual void visitName(QQmlJS::IR::Name *e);
+ virtual void visitTemp(QQmlJS::IR::Temp *e);
+ virtual void visitUnop(QQmlJS::IR::Unop *e);
+ virtual void visitBinop(QQmlJS::IR::Binop *e);
+ virtual void visitCall(QQmlJS::IR::Call *e);
+
+ //
+ // statements
+ //
+ virtual void visitExp(QQmlJS::IR::Exp *s);
+ virtual void visitMove(QQmlJS::IR::Move *s);
+ virtual void visitJump(QQmlJS::IR::Jump *s);
+ virtual void visitCJump(QQmlJS::IR::CJump *s);
+ virtual void visitRet(QQmlJS::IR::Ret *s);
+
+private:
+ int indentSize;
+ QByteArray indentData;
+ void inc();
+ void dec();
+};
+#endif
+
+} // end of namespace IR
+
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV4IR_P_H
diff --git a/src/qml/qml/v4/qv4irbuilder.cpp b/src/qml/qml/v4/qv4irbuilder.cpp
new file mode 100644
index 0000000000..1956be8e72
--- /dev/null
+++ b/src/qml/qml/v4/qv4irbuilder.cpp
@@ -0,0 +1,1303 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4irbuilder_p.h"
+#include "qv4compiler_p_p.h"
+
+#include <private/qqmlglobal_p.h>
+#include <private/qqmlmetatype_p.h>
+#include <private/qqmltypenamecache_p.h>
+
+DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER)
+
+QT_BEGIN_NAMESPACE
+
+using namespace QQmlJS;
+
+static IR::Type irTypeFromVariantType(int t, QQmlEnginePrivate *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 == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
+ return IR::SGAnchorLineType;
+ } else if (engine->metaObjectForType(t)) {
+ return IR::ObjectType;
+ }
+
+ return IR::InvalidType;
+ }
+}
+
+QV4IRBuilder::QV4IRBuilder(const QV4Compiler::Expression *expr,
+ QQmlEnginePrivate *engine)
+: m_expression(expr), m_engine(engine), _function(0), _block(0), _discard(false)
+{
+}
+
+bool QV4IRBuilder::operator()(QQmlJS::IR::Function *function,
+ QQmlJS::AST::Node *ast)
+{
+ bool discarded = false;
+
+ 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);
+
+ return !discarded;
+}
+
+bool QV4IRBuilder::buildName(QList<QStringRef> &name,
+ AST::Node *node,
+ QList<AST::ExpressionNode *> *nodes)
+{
+ if (node->kind == AST::Node::Kind_IdentifierExpression) {
+ name << static_cast<AST::IdentifierExpression*>(node)->name;
+ 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;
+ if (nodes) *nodes << expr;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void QV4IRBuilder::discard()
+{
+ _discard = true;
+}
+
+QV4IRBuilder::ExprResult
+QV4IRBuilder::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 QV4IRBuilder::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);
+ }
+}
+
+QV4IRBuilder::ExprResult
+QV4IRBuilder::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 QV4IRBuilder::sourceElement(AST::SourceElement *ast)
+{
+ accept(ast);
+}
+
+void QV4IRBuilder::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 QV4IRBuilder::visit(AST::UiProgram *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiImportList *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiImport *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiPublicMember *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiSourceElement *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiObjectDefinition *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiObjectInitializer *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiObjectBinding *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiScriptBinding *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiArrayBinding *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiObjectMemberList *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiArrayMemberList *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::UiQualifiedId *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+
+// JS
+bool QV4IRBuilder::visit(AST::Program *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::SourceElements *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::FunctionSourceElement *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::StatementSourceElement *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+// object literals
+bool QV4IRBuilder::visit(AST::PropertyNameAndValueList *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::IdentifierPropertyName *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::StringLiteralPropertyName *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::NumericLiteralPropertyName *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+
+// array literals
+bool QV4IRBuilder::visit(AST::ElementList *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::Elision *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+
+// function calls
+bool QV4IRBuilder::visit(AST::ArgumentList *)
+{
+ Q_ASSERT(!"unreachable");
+ return false;
+}
+
+// expressions
+bool QV4IRBuilder::visit(AST::ObjectLiteral *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::ArrayLiteral *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::ThisExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::IdentifierExpression *ast)
+{
+ const quint32 line = ast->identifierToken.startLine;
+ const quint32 column = ast->identifierToken.startColumn;
+
+ const QString name = ast->name.toString();
+
+ 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->v8engine()->illegalNames().contains(name) ) {
+ if (qmlVerboseCompiler()) qWarning() << "*** illegal symbol:" << name;
+ return false;
+ } else if (const QQmlScript::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 {
+
+ QQmlTypeNameCache::Result r = m_expression->importCache->query(name);
+ if (r.isValid()) {
+ if (r.type) {
+ _expr.code = _block->ATTACH_TYPE(name, r.type, IR::Name::ScopeStorage, line, column);
+ }
+ // We don't support anything else
+ } 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
+ QQmlPropertyCache *cache = m_expression->context->synthCache;
+ const QMetaObject *metaObject = m_expression->context->metaObject();
+ if (!cache) cache = m_engine->cache(metaObject);
+
+ QQmlPropertyData *data = cache->property(name);
+
+ if (data && data->revision != 0) {
+ if (qmlVerboseCompiler())
+ qWarning() << "*** versioned symbol:" << name;
+ discard();
+ return false;
+ }
+
+ if (data && !data->isFunction()) {
+ IR::Type irType = irTypeFromVariantType(data->propType, m_engine, metaObject);
+ _expr.code = _block->SYMBOL(irType, name, metaObject, data, IR::Name::ScopeStorage, line, column);
+ found = true;
+ }
+ }
+
+ if (!found) {
+ QQmlPropertyCache *cache = m_expression->component->synthCache;
+ const QMetaObject *metaObject = m_expression->component->metaObject();
+ if (!cache) cache = m_engine->cache(metaObject);
+
+ QQmlPropertyData *data = cache->property(name);
+
+ if (data && data->revision != 0) {
+ if (qmlVerboseCompiler())
+ qWarning() << "*** versioned symbol:" << name;
+ discard();
+ return false;
+ }
+
+ if (data && !data->isFunction()) {
+ IR::Type irType = irTypeFromVariantType(data->propType, m_engine, metaObject);
+ _expr.code = _block->SYMBOL(irType, name, metaObject, data, 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 QV4IRBuilder::visit(AST::NullExpression *)
+{
+ // ### TODO: cx format
+ _expr.code = _block->CONST(IR::NullType, 0);
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::TrueLiteral *)
+{
+ // ### TODO: cx format
+ _expr.code = _block->CONST(IR::BoolType, 1);
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::FalseLiteral *)
+{
+ // ### TODO: cx format
+ _expr.code = _block->CONST(IR::BoolType, 0);
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::StringLiteral *ast)
+{
+ // ### TODO: cx format
+ _expr.code = _block->STRING(ast->value);
+ return false;
+}
+
+bool QV4IRBuilder::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 QV4IRBuilder::visit(AST::RegExpLiteral *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::NestedExpression *)
+{
+ return true; // the value of the nested expression
+}
+
+bool QV4IRBuilder::visit(AST::ArrayMemberExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::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.toString();
+
+ 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.toString());
+ } else if(const QMetaObject *attachedMeta = baseName->declarativeType->attachedPropertiesType()) {
+ QQmlPropertyCache *cache = m_engine->cache(attachedMeta);
+ QQmlPropertyData *data = cache->property(name);
+
+ if (!data || data->isFunction())
+ return false; // Don't support methods (or non-existing properties ;)
+
+ if(!data->isFinal()) {
+ if (qmlVerboseCompiler())
+ qWarning() << "*** non-final attached property:"
+ << (*baseName->id + QLatin1String(".") + ast->name.toString());
+ 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, line, column);
+ }
+ break;
+
+ case IR::Name::IdObject: {
+ const QQmlScript::Object *idObject = baseName->idObject;
+ QQmlPropertyCache *cache =
+ idObject->synthCache?idObject->synthCache:m_engine->cache(idObject->metaObject());
+
+ QQmlPropertyData *data = cache->property(name);
+
+ if (!data || 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, line, column);
+ }
+ break;
+
+ case IR::Name::Property:
+ if (baseName->type == IR::ObjectType && baseName->meta && baseName->property->isFinal()) {
+ QQmlPropertyCache *cache = m_engine->cache(baseName->meta);
+ if (!cache)
+ return false;
+
+ if (QQmlPropertyData *data = cache->property(name)) {
+ if (!data->isFinal()) {
+ if (qmlVerboseCompiler())
+ qWarning() << "*** non-final property access:"
+ << (*baseName->id + QLatin1String(".") + ast->name.toString());
+ 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, line, column);
+ }
+ }
+ break;
+
+ case IR::Name::Object:
+ case IR::Name::Slot:
+ break;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool QV4IRBuilder::preVisit(AST::Node *)
+{
+ return ! _discard;
+}
+
+bool QV4IRBuilder::visit(AST::NewMemberExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::NewExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::CallExpression *ast)
+{
+ QList<QStringRef> names;
+ QList<AST::ExpressionNode *> nameNodes;
+
+ names.reserve(4);
+ nameNodes.reserve(4);
+
+ if (buildName(names, ast->base, &nameNodes)) {
+ //ExprResult base = expression(ast->base);
+ QString id;
+ for (int i = 0; i < names.size(); ++i) {
+ if (! i)
+ id += QLatin1Char('.');
+ id += names.at(i);
+ }
+ const AST::SourceLocation loc = nameNodes.last()->firstSourceLocation();
+ IR::Expr *base = _block->NAME(id, loc.startLine, loc.startColumn);
+
+ IR::ExprList *args = 0, **argsInserter = &args;
+ for (AST::ArgumentList *it = ast->arguments; it; it = it->next) {
+ IR::Expr *arg = expression(it->expression);
+ *argsInserter = _function->pool->New<IR::ExprList>();
+ (*argsInserter)->init(arg);
+ argsInserter = &(*argsInserter)->next;
+ }
+
+ 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 QV4IRBuilder::visit(AST::PostIncrementExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::PostDecrementExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::DeleteExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::VoidExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::TypeOfExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::PreIncrementExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::PreDecrementExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::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 QV4IRBuilder::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 QV4IRBuilder::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 QV4IRBuilder::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 QV4IRBuilder::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 QV4IRBuilder::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);
+ _block->JUMP(endif);
+
+ 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 QV4IRBuilder::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 QV4IRBuilder::visit(AST::Expression *ast)
+{
+ _block->EXP(expression(ast->left));
+ _expr = expression(ast->right);
+
+ return false;
+}
+
+
+// statements
+bool QV4IRBuilder::visit(AST::Block *ast)
+{
+ if (ast->statements && ! ast->statements->next) {
+ // we have one and only one statement
+ accept(ast->statements->statement);
+ }
+
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::StatementList *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::VariableStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::VariableDeclarationList *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::VariableDeclaration *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::EmptyStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::ExpressionStatement *ast)
+{
+ if (ast->expression) {
+ // return the value of this expression
+ return true;
+ }
+
+ return false;
+}
+
+bool QV4IRBuilder::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 QV4IRBuilder::visit(AST::DoWhileStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::WhileStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::ForStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::LocalForStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::ForEachStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::LocalForEachStatement *)
+{
+ discard();
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::ContinueStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::BreakStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::ReturnStatement *ast)
+{
+ if (ast->expression) {
+ // return the value of the expression
+ return true;
+ }
+
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::WithStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::SwitchStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::CaseBlock *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::CaseClauses *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::CaseClause *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::DefaultClause *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::LabelledStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::ThrowStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::TryStatement *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::Catch *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::Finally *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::FunctionDeclaration *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::FunctionExpression *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::FormalParameterList *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::FunctionBody *)
+{
+ return false;
+}
+
+bool QV4IRBuilder::visit(AST::DebuggerStatement *)
+{
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4irbuilder_p.h b/src/qml/qml/v4/qv4irbuilder_p.h
new file mode 100644
index 0000000000..2b338c0778
--- /dev/null
+++ b/src/qml/qml/v4/qv4irbuilder_p.h
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4IRBUILDER_P_H
+#define QV4IRBUILDER_P_H
+
+#include <QtCore/qglobal.h>
+
+#include "qv4ir_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QV4IRBuilder : public QQmlJS::AST::Visitor
+{
+public:
+ QV4IRBuilder(const QV4Compiler::Expression *, QQmlEnginePrivate *);
+
+ bool operator()(QQmlJS::IR::Function *, QQmlJS::AST::Node *);
+
+protected:
+ struct ExprResult {
+ enum Format {
+ ex, // expression
+ cx // condition
+ };
+
+ QQmlJS::IR::Expr *code;
+ QQmlJS::IR::BasicBlock *iftrue;
+ QQmlJS::IR::BasicBlock *iffalse;
+ Format hint; // requested format
+ Format format; // instruction format
+
+ ExprResult(QQmlJS::IR::Expr *expr = 0)
+ : code(expr), iftrue(0), iffalse(0), hint(ex), format(ex) {}
+
+ ExprResult(QQmlJS::IR::BasicBlock *iftrue, QQmlJS::IR::BasicBlock *iffalse)
+ : code(0), iftrue(iftrue), iffalse(iffalse), hint(cx), format(ex) {}
+
+ inline QQmlJS::IR::Type type() const { return code ? code->type : QQmlJS::IR::InvalidType; }
+
+ inline QQmlJS::IR::Expr *get() const { return code; }
+ inline operator QQmlJS::IR::Expr *() const { return get(); }
+ inline QQmlJS::IR::Expr *operator->() const { return get(); }
+ inline bool isValid() const { return code ? code->type != QQmlJS::IR::InvalidType : false; }
+ inline bool is(QQmlJS::IR::Type t) const { return type() == t; }
+ inline bool isNot(QQmlJS::IR::Type t) const { return type() != t; }
+
+ bool isPrimitive() const {
+ switch (type()) {
+ case QQmlJS::IR::UndefinedType: // ### TODO
+ case QQmlJS::IR::NullType: // ### TODO
+ case QQmlJS::IR::UrlType: // ### TODO
+ return false;
+
+ case QQmlJS::IR::StringType:
+ case QQmlJS::IR::BoolType:
+ case QQmlJS::IR::IntType:
+ case QQmlJS::IR::RealType:
+ case QQmlJS::IR::RealNaNType:
+ return true;
+
+ default:
+ return false;
+ } // switch
+ }
+ };
+
+ inline void accept(QQmlJS::AST::Node *ast) { QQmlJS::AST::Node::accept(ast, this); }
+
+ ExprResult expression(QQmlJS::AST::ExpressionNode *ast);
+ ExprResult statement(QQmlJS::AST::Statement *ast);
+ void sourceElement(QQmlJS::AST::SourceElement *ast);
+ void condition(QQmlJS::AST::ExpressionNode *ast, QQmlJS::IR::BasicBlock *iftrue, QQmlJS::IR::BasicBlock *iffalse);
+ void binop(QQmlJS::AST::BinaryExpression *ast, ExprResult left, ExprResult right);
+
+ void implicitCvt(ExprResult &expr, QQmlJS::IR::Type type);
+
+ virtual bool preVisit(QQmlJS::AST::Node *ast);
+
+ // QML
+ virtual bool visit(QQmlJS::AST::UiProgram *ast);
+ virtual bool visit(QQmlJS::AST::UiImportList *ast);
+ virtual bool visit(QQmlJS::AST::UiImport *ast);
+ virtual bool visit(QQmlJS::AST::UiPublicMember *ast);
+ virtual bool visit(QQmlJS::AST::UiSourceElement *ast);
+ virtual bool visit(QQmlJS::AST::UiObjectDefinition *ast);
+ virtual bool visit(QQmlJS::AST::UiObjectInitializer *ast);
+ virtual bool visit(QQmlJS::AST::UiObjectBinding *ast);
+ virtual bool visit(QQmlJS::AST::UiScriptBinding *ast);
+ virtual bool visit(QQmlJS::AST::UiArrayBinding *ast);
+ virtual bool visit(QQmlJS::AST::UiObjectMemberList *ast);
+ virtual bool visit(QQmlJS::AST::UiArrayMemberList *ast);
+ virtual bool visit(QQmlJS::AST::UiQualifiedId *ast);
+
+ // JS
+ virtual bool visit(QQmlJS::AST::Program *ast);
+ virtual bool visit(QQmlJS::AST::SourceElements *ast);
+ virtual bool visit(QQmlJS::AST::FunctionSourceElement *ast);
+ virtual bool visit(QQmlJS::AST::StatementSourceElement *ast);
+
+ // object literals
+ virtual bool visit(QQmlJS::AST::PropertyNameAndValueList *ast);
+ virtual bool visit(QQmlJS::AST::IdentifierPropertyName *ast);
+ virtual bool visit(QQmlJS::AST::StringLiteralPropertyName *ast);
+ virtual bool visit(QQmlJS::AST::NumericLiteralPropertyName *ast);
+
+ // array literals
+ virtual bool visit(QQmlJS::AST::ElementList *ast);
+ virtual bool visit(QQmlJS::AST::Elision *ast);
+
+ // function calls
+ virtual bool visit(QQmlJS::AST::ArgumentList *ast);
+
+ // expressions
+ virtual bool visit(QQmlJS::AST::ObjectLiteral *ast);
+ virtual bool visit(QQmlJS::AST::ArrayLiteral *ast);
+ virtual bool visit(QQmlJS::AST::ThisExpression *ast);
+ virtual bool visit(QQmlJS::AST::IdentifierExpression *ast);
+ virtual bool visit(QQmlJS::AST::NullExpression *ast);
+ virtual bool visit(QQmlJS::AST::TrueLiteral *ast);
+ virtual bool visit(QQmlJS::AST::FalseLiteral *ast);
+ virtual bool visit(QQmlJS::AST::StringLiteral *ast);
+ virtual bool visit(QQmlJS::AST::NumericLiteral *ast);
+ virtual bool visit(QQmlJS::AST::RegExpLiteral *ast);
+ virtual bool visit(QQmlJS::AST::NestedExpression *ast);
+ virtual bool visit(QQmlJS::AST::ArrayMemberExpression *ast);
+ virtual bool visit(QQmlJS::AST::FieldMemberExpression *ast);
+ virtual bool visit(QQmlJS::AST::NewMemberExpression *ast);
+ virtual bool visit(QQmlJS::AST::NewExpression *ast);
+ virtual bool visit(QQmlJS::AST::CallExpression *ast);
+ virtual bool visit(QQmlJS::AST::PostIncrementExpression *ast);
+ virtual bool visit(QQmlJS::AST::PostDecrementExpression *ast);
+ virtual bool visit(QQmlJS::AST::DeleteExpression *ast);
+ virtual bool visit(QQmlJS::AST::VoidExpression *ast);
+ virtual bool visit(QQmlJS::AST::TypeOfExpression *ast);
+ virtual bool visit(QQmlJS::AST::PreIncrementExpression *ast);
+ virtual bool visit(QQmlJS::AST::PreDecrementExpression *ast);
+ virtual bool visit(QQmlJS::AST::UnaryPlusExpression *ast);
+ virtual bool visit(QQmlJS::AST::UnaryMinusExpression *ast);
+ virtual bool visit(QQmlJS::AST::TildeExpression *ast);
+ virtual bool visit(QQmlJS::AST::NotExpression *ast);
+ virtual bool visit(QQmlJS::AST::BinaryExpression *ast);
+ virtual bool visit(QQmlJS::AST::ConditionalExpression *ast);
+ virtual bool visit(QQmlJS::AST::Expression *ast);
+
+ // statements
+ virtual bool visit(QQmlJS::AST::Block *ast);
+ virtual bool visit(QQmlJS::AST::StatementList *ast);
+ virtual bool visit(QQmlJS::AST::VariableStatement *ast);
+ virtual bool visit(QQmlJS::AST::VariableDeclarationList *ast);
+ virtual bool visit(QQmlJS::AST::VariableDeclaration *ast);
+ virtual bool visit(QQmlJS::AST::EmptyStatement *ast);
+ virtual bool visit(QQmlJS::AST::ExpressionStatement *ast);
+ virtual bool visit(QQmlJS::AST::IfStatement *ast);
+ virtual bool visit(QQmlJS::AST::DoWhileStatement *ast);
+ virtual bool visit(QQmlJS::AST::WhileStatement *ast);
+ virtual bool visit(QQmlJS::AST::ForStatement *ast);
+ virtual bool visit(QQmlJS::AST::LocalForStatement *ast);
+ virtual bool visit(QQmlJS::AST::ForEachStatement *ast);
+ virtual bool visit(QQmlJS::AST::LocalForEachStatement *ast);
+ virtual bool visit(QQmlJS::AST::ContinueStatement *ast);
+ virtual bool visit(QQmlJS::AST::BreakStatement *ast);
+ virtual bool visit(QQmlJS::AST::ReturnStatement *ast);
+ virtual bool visit(QQmlJS::AST::WithStatement *ast);
+ virtual bool visit(QQmlJS::AST::SwitchStatement *ast);
+ virtual bool visit(QQmlJS::AST::CaseBlock *ast);
+ virtual bool visit(QQmlJS::AST::CaseClauses *ast);
+ virtual bool visit(QQmlJS::AST::CaseClause *ast);
+ virtual bool visit(QQmlJS::AST::DefaultClause *ast);
+ virtual bool visit(QQmlJS::AST::LabelledStatement *ast);
+ virtual bool visit(QQmlJS::AST::ThrowStatement *ast);
+ virtual bool visit(QQmlJS::AST::TryStatement *ast);
+ virtual bool visit(QQmlJS::AST::Catch *ast);
+ virtual bool visit(QQmlJS::AST::Finally *ast);
+ virtual bool visit(QQmlJS::AST::FunctionDeclaration *ast);
+ virtual bool visit(QQmlJS::AST::FunctionExpression *ast);
+ virtual bool visit(QQmlJS::AST::FormalParameterList *ast);
+ virtual bool visit(QQmlJS::AST::FunctionBody *ast);
+ virtual bool visit(QQmlJS::AST::DebuggerStatement *ast);
+
+private:
+ bool buildName(QList<QStringRef> &name, QQmlJS::AST::Node *node,
+ QList<QQmlJS::AST::ExpressionNode *> *nodes);
+ void discard();
+
+ const QV4Compiler::Expression *m_expression;
+ QQmlEnginePrivate *m_engine;
+
+ QQmlJS::IR::Function *_function;
+ QQmlJS::IR::BasicBlock *_block;
+ bool _discard;
+
+ ExprResult _expr;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV4IRBUILDER_P_H
diff --git a/src/qml/qml/v4/qv4program_p.h b/src/qml/qml/v4/qv4program_p.h
new file mode 100644
index 0000000000..b6b03e438b
--- /dev/null
+++ b/src/qml/qml/v4/qv4program_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4PROGRAM_P_H
+#define QV4PROGRAM_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 "qv4instruction_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+struct QV4Program {
+ 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 QQmlRegisterType {
+ UndefinedType,
+ QObjectStarType,
+ QRealType,
+ IntType,
+ BoolType,
+
+ PODValueType,
+
+ FirstCleanupType,
+ QStringType = FirstCleanupType,
+ QUrlType,
+ QVariantType,
+};
+
+const char *QV4Program::data() const
+{
+ return ((const char *)this) + sizeof(QV4Program);
+}
+
+const char *QV4Program::instructions() const
+{
+ return (const char *)(data() + dataLength);
+}
+
+QV4Program::BindingReferenceList *QV4Program::signalTable(int signalIndex) const
+{
+ quint32 *signalTable = (quint32 *)(data() + signalTableOffset);
+ return (BindingReferenceList *)(signalTable + signalTable[signalIndex]);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV4PROGRAM_P_H
+
diff --git a/src/qml/qml/v4/v4.pri b/src/qml/qml/v4/v4.pri
new file mode 100644
index 0000000000..b6784851d8
--- /dev/null
+++ b/src/qml/qml/v4/v4.pri
@@ -0,0 +1,15 @@
+HEADERS += \
+ $$PWD/qv4compiler_p.h \
+ $$PWD/qv4compiler_p_p.h \
+ $$PWD/qv4ir_p.h \
+ $$PWD/qv4irbuilder_p.h \
+ $$PWD/qv4instruction_p.h \
+ $$PWD/qv4bindings_p.h \
+ $$PWD/qv4program_p.h \
+
+SOURCES += \
+ $$PWD/qv4compiler.cpp \
+ $$PWD/qv4ir.cpp \
+ $$PWD/qv4irbuilder.cpp \
+ $$PWD/qv4instruction.cpp \
+ $$PWD/qv4bindings.cpp \