diff options
Diffstat (limited to 'src/qml/qml/v4')
-rw-r--r-- | src/qml/qml/v4/qv4bindings.cpp | 1538 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4bindings_p.h | 153 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4compiler.cpp | 1399 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4compiler_p.h | 105 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4compiler_p_p.h | 245 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4instruction.cpp | 412 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4instruction_p.h | 432 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4ir.cpp | 882 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4ir_p.h | 604 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4irbuilder.cpp | 1303 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4irbuilder_p.h | 240 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4program_p.h | 122 | ||||
-rw-r--r-- | src/qml/qml/v4/v4.pri | 15 |
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 ® = 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, ¬ifier); + 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 ® = 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 = ®isteredStrings[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 \ |