diff options
Diffstat (limited to 'src/qml/qml/v4/qv4compiler.cpp')
-rw-r--r-- | src/qml/qml/v4/qv4compiler.cpp | 1420 |
1 files changed, 1420 insertions, 0 deletions
diff --git a/src/qml/qml/v4/qv4compiler.cpp b/src/qml/qml/v4/qv4compiler.cpp new file mode 100644 index 0000000000..f8fe3b4b57 --- /dev/null +++ b/src/qml/qml/v4/qv4compiler.cpp @@ -0,0 +1,1420 @@ +/**************************************************************************** +** +** 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), registerCount(0) +{ +} + +// +// tracing +// +void QV4CompilerPrivate::trace(int line, int column) +{ + bytecode.clear(); + + this->currentReg = _function->tempCount; + this->registerCount = qMax(this->registerCount, this->currentReg); + + 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; + case QMetaType::QColor: + regType = QColorType; + 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; + + case IR::ColorType: { + Instr::ConvertColorToBool 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; + case IR::ColorType: opcode = V4Instr::ConvertColorToBool; 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; + case IR::ColorType: opcode = V4Instr::ConvertColorToString; 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; + case IR::ColorType: gen(V4Instr::ConvertColorToString, convToString); sourceTy = IR::StringType; break; + default: break; + } // switch + + if (sourceTy == IR::StringType) + opcode = V4Instr::ConvertStringToUrl; + } else if (targetTy == IR::ColorType) { + switch (sourceTy) { + case IR::StringType: opcode = V4Instr::ConvertStringToColor; break; + default: break; + } // switch + } + 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::ColorType: + test.regType = QMetaType::QColor; + 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 || registerCount > 31) + 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 |