1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qv4compiler_p.h"
43 #include "qv4compiler_p_p.h"
44 #include "qv4program_p.h"
46 #include "qv4irbuilder_p.h"
48 #include <private/qqmlglobal_p.h>
49 #include <private/qqmljsast_p.h>
50 #include <private/qqmlaccessors_p.h>
51 #include <private/qqmljsengine_p.h>
53 Q_DECLARE_METATYPE(QJSValue)
57 DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP)
58 DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER)
59 DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER)
60 DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST)
62 static bool qmlBindingsTest = false;
63 static bool qmlEnableV4 = true;
65 using namespace QQmlJS;
66 QV4CompilerPrivate::QV4CompilerPrivate()
67 : subscriptionOffset(0)
68 , _function(0) , _block(0) , _discarded(false), registerCount(0)
69 , bindingLine(0), bindingColumn(0), invalidatable(false)
76 void QV4CompilerPrivate::trace(quint16 line, quint16 column)
80 this->bindingLine = line;
81 this->bindingColumn = column;
82 this->currentReg = _function->tempCount;
83 this->registerCount = qMax(this->registerCount, this->currentReg);
85 foreach (IR::BasicBlock *bb, _function->basicBlocks) {
86 if (! bb->isTerminated() && (bb->index + 1) < _function->basicBlocks.size())
87 bb->JUMP(_function->basicBlocks.at(bb->index + 1));
90 QVector<IR::BasicBlock *> blocks;
92 currentBlockMask = 0x00000001;
95 for (int i = 0; !_discarded && i < blocks.size(); ++i) {
96 IR::BasicBlock *block = blocks.at(i);
97 IR::BasicBlock *next = i + 1 < blocks.size() ? blocks.at(i + 1) : 0;
98 if (IR::Stmt *terminator = block->terminator()) {
99 if (IR::CJump *cj = terminator->asCJump()) {
100 if (cj->iffalse != next) {
101 IR::Jump *jump = _function->pool->New<IR::Jump>();
102 jump->init(cj->iffalse);
103 block->statements.append(jump);
105 } else if (IR::Jump *j = terminator->asJump()) {
106 if (j->target == next) {
107 block->statements.resize(block->statements.size() - 1);
112 block->offset = bytecode.size();
114 if (bytecode.isEmpty()) {
115 if (qmlBindingsTest || bindingsDump()) {
122 if (qmlBindingsTest) {
123 QString str = expression->expression.asScript();
124 QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
125 int offset = data.count();
128 Instr::EnableV4Test test;
130 test.offset = offset;
131 test.length = str.length();
137 int patchesCount = patches.count();
138 qSwap(usedSubscriptionIdsChanged, usic);
140 int blockopIndex = bytecode.size();
141 Instr::Block blockop;
142 blockop.block = currentBlockMask;
145 foreach (IR::Stmt *s, block->statements) {
150 qSwap(usedSubscriptionIdsChanged, usic);
153 if (currentBlockMask == 0x80000000) {
157 currentBlockMask <<= 1;
158 } else if (! _discarded) {
159 const int adjust = bytecode.remove(blockopIndex);
161 for (int ii = patchesCount; ii < patches.count(); ++ii)
162 patches[ii].offset -= adjust;
166 #ifdef DEBUG_IR_STRUCTURE
168 for (int i = 0; i < blocks.size(); ++i) {
169 dump.basicblock(blocks.at(i));
176 foreach (const Patch &patch, patches) {
177 V4Instr &instr = bytecode[patch.offset];
178 int size = V4Instr::size(instructionType(&instr));
179 instr.branchop.offset = patch.block->offset - patch.offset - size;
186 void QV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks)
188 for (int i = 0; i < _function->basicBlocks.size(); ++i) {
189 IR::BasicBlock *block = _function->basicBlocks.at(i);
191 while (! blocks->contains(block)) {
192 blocks->append(block);
194 if (IR::Stmt *terminator = block->terminator()) {
195 if (IR::CJump *cj = terminator->asCJump())
197 else if (IR::Jump *j = terminator->asJump())
204 void QV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r)
209 qSwap(currentReg, r);
211 qSwap(currentReg, r);
218 void QV4CompilerPrivate::visitConst(IR::Const *e)
236 case IR::NumberType: {
250 if (qmlVerboseCompiler())
251 qWarning() << Q_FUNC_INFO << "unexpected type";
256 void QV4CompilerPrivate::visitString(IR::String *e)
258 registerLiteralString(currentReg, e->value);
261 void QV4CompilerPrivate::visitName(IR::Name *e)
264 // fetch the object and store it in reg.
265 traceExpression(e->base, currentReg);
267 _subscribeName.clear();
270 if (e->storage == IR::Name::RootStorage) {
272 Instr::LoadRoot instr;
273 instr.reg = currentReg;
276 if (e->symbol == IR::Name::IdObject) {
277 // The ID is a reference to the root object
281 } else if (e->storage == IR::Name::ScopeStorage) {
283 Instr::LoadScope instr;
284 instr.reg = currentReg;
287 _subscribeName << contextName();
289 } else if (e->storage == IR::Name::IdStorage) {
292 instr.reg = currentReg;
293 instr.index = e->idObject->idIndex;
296 _subscribeName << QLatin1String("$$$ID_") + *e->id;
298 if (blockNeedsSubscription(_subscribeName)) {
299 Instr::SubscribeId sub;
300 sub.reg = currentReg;
301 sub.offset = subscriptionIndex(_subscribeName);
302 sub.index = instr.index;
312 case IR::Name::Unbound:
313 case IR::Name::IdObject:
315 case IR::Name::Object: {
316 Q_ASSERT(!"Unreachable");
320 case IR::Name::AttachType: {
321 _subscribeName << *e->id;
323 Instr::LoadAttached attached;
324 attached.output = currentReg;
325 attached.reg = currentReg;
326 attached.exceptionId = exceptionId(bindingLine, bindingColumn);
327 if (e->declarativeType->attachedPropertiesId() == -1)
329 attached.id = e->declarativeType->attachedPropertiesId();
333 case IR::Name::SingletonObject: {
335 Existing singleton type object lookup methods include:
336 1. string -> singleton object (search via importCache->query(name))
337 2. typeid -> singleton object QQmlType (search via ???)
338 We currently use 1, which is not ideal for performance
340 _subscribeName << *e->id;
342 registerLiteralString(currentReg, e->id);
344 Instr::LoadSingletonObject module;
345 module.reg = currentReg;
349 case IR::Name::Property: {
350 _subscribeName << *e->id;
352 if (e->property->coreIndex == -1) {
354 e->property->load(prop, QQmlEnginePrivate::get(engine));
357 const int propTy = e->property->propType;
358 QQmlRegisterType regType;
361 case QMetaType::Float:
364 case QMetaType::Double:
365 regType = NumberType;
367 case QMetaType::Bool:
373 case QMetaType::QString:
374 regType = QStringType;
376 case QMetaType::QUrl:
379 case QMetaType::QColor:
380 regType = QColorType;
382 case QMetaType::QVariant:
383 regType = QVariantType;
387 if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
388 regType = PODValueType;
389 } else if (!engine->metaObjectForType(propTy).isNull()) {
390 regType = QObjectStarType;
392 if (qmlVerboseCompiler())
393 qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy);
394 discard(); // Unsupported type
401 if (e->property->hasAccessors()) {
402 Instr::FetchAndSubscribe fetch;
403 fetch.reg = currentReg;
404 fetch.subscription = subscriptionIndex(_subscribeName);
405 fetch.exceptionId = exceptionId(e->line, e->column);
406 fetch.valueType = regType;
407 fetch.property = *e->property;
411 fetch.reg = currentReg;
412 fetch.index = e->property->coreIndex;
413 fetch.exceptionId = exceptionId(e->line, e->column);
414 fetch.valueType = regType;
416 if (blockNeedsSubscription(_subscribeName) && e->property->notifyIndex != -1) {
417 fetch.subOffset = subscriptionIndex(_subscribeName);
418 fetch.subIndex = e->property->notifyIndex;
420 fetch.subOffset = static_cast<quint16>(-1);
421 fetch.subIndex = static_cast<quint32>(-1);
431 void QV4CompilerPrivate::visitTemp(IR::Temp *e)
433 if (currentReg != e->index) {
441 void QV4CompilerPrivate::visitUnop(IR::Unop *e)
443 quint8 src = currentReg;
445 if (IR::Temp *temp = e->expr->asTemp()) {
448 traceExpression(e->expr, src);
453 Q_ASSERT(!"unreachable");
457 convertToBool(e->expr, src);
458 if (src != currentReg) {
468 convertToBool(e->expr, src);
469 i.output = currentReg;
475 if (IR::isRealType(e->expr->type)) {
476 Instr::UnaryMinusNumber i;
477 i.output = currentReg;
480 } else if (e->expr->type == IR::IntType) {
481 convertToNumber(e->expr, currentReg);
482 Instr::UnaryMinusNumber i;
483 i.output = currentReg;
492 if (IR::isRealType(e->expr->type)) {
493 Instr::UnaryPlusNumber i;
494 i.output = currentReg;
497 } else if (e->expr->type == IR::IntType) {
498 convertToNumber(e->expr, currentReg);
499 Instr::UnaryPlusNumber i;
500 i.output = currentReg;
530 case IR::OpStrictEqual:
531 case IR::OpStrictNotEqual:
534 Q_ASSERT(!"unreachable");
539 void QV4CompilerPrivate::convertToNumber(IR::Expr *expr, int reg)
541 if (expr->type == IR::NumberType)
544 switch (expr->type) {
546 Instr::ConvertBoolToNumber i;
547 i.output = i.src = reg;
552 Instr::ConvertIntToNumber i;
553 i.output = i.src = reg;
568 void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg)
570 if (expr->type == IR::IntType)
573 switch (expr->type) {
575 Instr::ConvertBoolToInt i;
576 i.output = i.src = reg;
585 case IR::NumberType: {
586 Instr::ConvertNumberToInt i;
587 i.output = i.src = reg;
597 void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg)
599 if (expr->type == IR::BoolType)
602 switch (expr->type) {
608 Instr::ConvertIntToBool i;
609 i.output = i.src = reg;
614 case IR::NumberType: {
615 Instr::ConvertNumberToBool i;
616 i.output = i.src = reg;
620 case IR::StringType: {
621 Instr::ConvertStringToBool i;
622 i.output = i.src = reg;
626 case IR::ColorType: {
627 Instr::ConvertColorToBool i;
628 i.output = i.src = reg;
638 quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e)
642 return V4Instr::Noop;
649 return V4Instr::Noop;
652 return V4Instr::BitAndInt;
655 return V4Instr::BitOrInt;
658 return V4Instr::BitXorInt;
661 if (e->type == IR::StringType)
662 return V4Instr::AddString;
663 return V4Instr::AddNumber;
666 return V4Instr::SubNumber;
669 return V4Instr::MulNumber;
672 return V4Instr::DivNumber;
675 return V4Instr::ModNumber;
678 return V4Instr::LShiftInt;
681 return V4Instr::RShiftInt;
684 return V4Instr::URShiftInt;
687 if (e->left->type == IR::StringType)
688 return V4Instr::GtString;
689 return V4Instr::GtNumber;
692 if (e->left->type == IR::StringType)
693 return V4Instr::LtString;
694 return V4Instr::LtNumber;
697 if (e->left->type == IR::StringType)
698 return V4Instr::GeString;
699 return V4Instr::GeNumber;
702 if (e->left->type == IR::StringType)
703 return V4Instr::LeString;
704 return V4Instr::LeNumber;
707 if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
708 return V4Instr::EqualObject;
709 if (e->left->type == IR::StringType)
710 return V4Instr::EqualString;
711 return V4Instr::EqualNumber;
714 if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
715 return V4Instr::NotEqualObject;
716 if (e->left->type == IR::StringType)
717 return V4Instr::NotEqualString;
718 return V4Instr::NotEqualNumber;
720 case IR::OpStrictEqual:
721 if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
722 return V4Instr::StrictEqualObject;
723 if (e->left->type == IR::StringType)
724 return V4Instr::StrictEqualString;
725 return V4Instr::StrictEqualNumber;
727 case IR::OpStrictNotEqual:
728 if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
729 return V4Instr::StrictNotEqualObject;
730 if (e->left->type == IR::StringType)
731 return V4Instr::StrictNotEqualString;
732 return V4Instr::StrictNotEqualNumber;
736 return V4Instr::Noop;
740 return V4Instr::Noop;
743 void QV4CompilerPrivate::visitBinop(IR::Binop *e)
745 if (e->type == IR::InvalidType) {
750 int left = currentReg;
751 int right = currentReg + 1;
753 if (e->left->asTemp() && e->type != IR::StringType)
754 left = e->left->asTemp()->index;
756 traceExpression(e->left, left);
758 if (IR::Temp *t = e->right->asTemp())
761 traceExpression(e->right, right);
763 if (e->left->type != e->right->type) {
764 if (qmlVerboseCompiler())
765 qWarning().nospace() << "invalid operands to binary operator " << IR::binaryOperator(e->op)
766 << "(`" << IR::binaryOperator(e->left->type)
768 << IR::binaryOperator(e->right->type)
794 convertToInt(e->left, left);
795 convertToInt(e->right, right);
799 if (e->type != IR::StringType) {
800 convertToNumber(e->left, left);
801 convertToNumber(e->right, right);
809 convertToNumber(e->left, left);
810 convertToNumber(e->right, right);
819 case IR::OpStrictEqual:
820 case IR::OpStrictNotEqual:
821 if (e->left->type >= IR::FirstNumberType) {
822 convertToNumber(e->left, left);
823 convertToNumber(e->right, right);
829 discard(); // ### unreachable
833 const quint8 opcode = instructionOpcode(e);
834 if (opcode != V4Instr::Noop) {
836 instr.binaryop.output = currentReg;
837 instr.binaryop.left = left;
838 instr.binaryop.right = right;
839 gen(static_cast<V4Instr::Type>(opcode), instr);
843 void QV4CompilerPrivate::visitCall(IR::Call *call)
845 if (IR::Name *name = call->base->asName()) {
846 IR::Expr *arg = call->onlyArgument();
847 if (arg != 0 && IR::isRealType(arg->type)) {
848 traceExpression(arg, currentReg);
850 switch (name->builtin) {
851 case IR::NoBuiltinSymbol:
854 case IR::MathSinBultinFunction: {
855 Instr::MathSinNumber i;
856 i.output = i.src = currentReg;
860 case IR::MathCosBultinFunction: {
861 Instr::MathCosNumber i;
862 i.output = i.src = currentReg;
866 case IR::MathAbsBuiltinFunction: {
867 Instr::MathAbsNumber i;
868 i.output = i.src = currentReg;
872 case IR::MathRoundBultinFunction: {
873 Instr::MathRoundNumber i;
874 i.output = i.src = currentReg;
878 case IR::MathFloorBultinFunction: {
879 Instr::MathFloorNumber i;
880 i.output = i.src = currentReg;
884 case IR::MathCeilBuiltinFunction: {
885 Instr::MathCeilNumber i;
886 i.output = i.src = currentReg;
890 case IR::MathPIBuiltinConstant:
895 if (name->builtin == IR::MathMaxBuiltinFunction ||
896 name->builtin == IR::MathMinBuiltinFunction) {
898 //only handles the most common case of exactly two arguments
899 if (call->args && call->args->next && !call->args->next->next) {
900 IR::Expr *arg1 = call->args->expr;
901 IR::Expr *arg2 = call->args->next->expr;
903 if (arg1 != 0 && IR::isRealType(arg1->type) &&
904 arg2 != 0 && IR::isRealType(arg2->type)) {
906 traceExpression(arg1, currentReg);
907 traceExpression(arg2, currentReg + 1);
909 if (name->builtin == IR::MathMaxBuiltinFunction) {
910 Instr::MathMaxNumber i;
912 i.right = currentReg + 1;
913 i.output = currentReg;
916 } else if (name->builtin == IR::MathMinBuiltinFunction) {
917 Instr::MathMinNumber i;
919 i.right = currentReg + 1;
920 i.output = currentReg;
930 if (qmlVerboseCompiler())
931 qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__;
939 void QV4CompilerPrivate::visitExp(IR::Exp *s)
941 traceExpression(s->expr, currentReg);
944 void QV4CompilerPrivate::visitMove(IR::Move *s)
946 IR::Temp *target = s->target->asTemp();
947 Q_ASSERT(target != 0);
949 quint8 dest = target->index;
951 IR::Type targetTy = s->target->type;
952 IR::Type sourceTy = s->source->type;
954 // promote the floats
955 if (sourceTy == IR::FloatType)
956 sourceTy = IR::NumberType;
958 if (targetTy == IR::FloatType)
959 targetTy = IR::NumberType;
961 if (sourceTy != targetTy) {
964 if (IR::Temp *t = s->source->asTemp())
967 traceExpression(s->source, dest);
969 V4Instr::Type opcode = V4Instr::Noop;
971 if (sourceTy == IR::UrlType) {
975 case IR::VariantType:
977 case IR::JSValueType:
978 // nothing to do. V4 will generate optimized
979 // url-to-xxx conversions.
982 if (s->isMoveForReturn) {
984 instr.throwop.exceptionId = exceptionId(bindingLine, bindingColumn);
985 registerLiteralString(dest, _function->newString(QString::fromUtf8("Unable to assign %1 to %2")
986 .arg(QLatin1String(IR::typeName(sourceTy)))
987 .arg(QLatin1String(IR::typeName(targetTy)))));
988 instr.throwop.message = dest;
989 gen(V4Instr::Throw, instr);
992 // generate a UrlToString conversion and fix
993 // the type of the source expression.
995 conv.unaryop.output = src;
996 conv.unaryop.src = src;
997 gen(V4Instr::ConvertUrlToString, conv);
998 sourceTy = IR::StringType;
1004 if (targetTy == IR::BoolType) {
1006 case IR::IntType: opcode = V4Instr::ConvertIntToBool; break;
1007 case IR::NumberType: opcode = V4Instr::ConvertNumberToBool; break;
1008 case IR::StringType: opcode = V4Instr::ConvertStringToBool; break;
1009 case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break;
1010 case IR::ColorType: opcode = V4Instr::ConvertColorToBool; break;
1011 case IR::ObjectType: opcode = V4Instr::ConvertObjectToBool; break;
1014 } else if (targetTy == IR::IntType) {
1016 case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break;
1017 case IR::NumberType: {
1018 if (s->isMoveForReturn)
1019 opcode = V4Instr::MathRoundNumber;
1021 opcode = V4Instr::ConvertNumberToInt;
1024 case IR::StringType: opcode = V4Instr::ConvertStringToInt; break;
1027 } else if (IR::isRealType(targetTy)) {
1029 case IR::BoolType: opcode = V4Instr::ConvertBoolToNumber; break;
1030 case IR::IntType: opcode = V4Instr::ConvertIntToNumber; break;
1031 case IR::StringType: opcode = V4Instr::ConvertStringToNumber; break;
1034 } else if (targetTy == IR::StringType) {
1036 case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break;
1037 case IR::IntType: opcode = V4Instr::ConvertIntToString; break;
1038 case IR::NumberType: opcode = V4Instr::ConvertNumberToString; break;
1039 case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break;
1040 case IR::ColorType: opcode = V4Instr::ConvertColorToString; break;
1043 } else if (targetTy == IR::UrlType) {
1044 if (s->isMoveForReturn && sourceTy != IR::StringType) {
1046 instr.throwop.exceptionId = exceptionId(bindingLine, bindingColumn);
1047 registerLiteralString(dest, _function->newString(QString::fromUtf8("Unable to assign %1 to %2")
1048 .arg(QLatin1String(IR::typeName(sourceTy)))
1049 .arg(QLatin1String(IR::typeName(targetTy)))));
1050 instr.throwop.message = dest;
1051 gen(V4Instr::Throw, instr);
1055 V4Instr convToString;
1056 convToString.unaryop.output = dest;
1057 convToString.unaryop.src = src;
1059 // try to convert the source expression to a string.
1061 case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break;
1062 case IR::IntType: gen(V4Instr::ConvertIntToString, convToString); sourceTy = IR::StringType; break;
1063 case IR::NumberType: gen(V4Instr::ConvertNumberToString, convToString); sourceTy = IR::StringType; break;
1064 case IR::ColorType: gen(V4Instr::ConvertColorToString, convToString); sourceTy = IR::StringType; break;
1068 if (sourceTy == IR::StringType)
1069 opcode = V4Instr::ConvertStringToUrl;
1070 } else if (targetTy == IR::ColorType) {
1072 case IR::StringType: opcode = V4Instr::ConvertStringToColor; break;
1075 } else if (targetTy == IR::ObjectType) {
1077 case IR::NullType: opcode = V4Instr::ConvertNullToObject; break;
1080 } else if (targetTy == IR::VariantType) {
1081 if (s->isMoveForReturn) {
1083 case IR::BoolType: opcode = V4Instr::ConvertBoolToVariant; break;
1084 case IR::IntType: opcode = V4Instr::ConvertIntToVariant; break;
1085 case IR::NumberType: opcode = V4Instr::ConvertNumberToVariant; break;
1086 case IR::UrlType: opcode = V4Instr::ConvertUrlToVariant; break;
1087 case IR::ColorType: opcode = V4Instr::ConvertColorToVariant; break;
1088 case IR::StringType: opcode = V4Instr::ConvertStringToVariant; break;
1089 case IR::ObjectType: opcode = V4Instr::ConvertObjectToVariant; break;
1090 case IR::NullType: opcode = V4Instr::ConvertNullToVariant; break;
1094 } else if (targetTy == IR::VarType) {
1095 if (s->isMoveForReturn) {
1097 case IR::BoolType: opcode = V4Instr::ConvertBoolToVar; break;
1098 case IR::IntType: opcode = V4Instr::ConvertIntToVar; break;
1099 case IR::NumberType: opcode = V4Instr::ConvertNumberToVar; break;
1100 case IR::UrlType: opcode = V4Instr::ConvertUrlToVar; break;
1101 case IR::ColorType: opcode = V4Instr::ConvertColorToVar; break;
1102 case IR::StringType: opcode = V4Instr::ConvertStringToVar; break;
1103 case IR::ObjectType: opcode = V4Instr::ConvertObjectToVar; break;
1104 case IR::NullType: opcode = V4Instr::ConvertNullToVar; break;
1105 case IR::JSValueType: opcode = V4Instr::ConvertJSValueToVar; break;
1109 } else if (targetTy == IR::JSValueType) {
1110 if (s->isMoveForReturn) {
1112 case IR::BoolType: opcode = V4Instr::ConvertBoolToJSValue; break;
1113 case IR::IntType: opcode = V4Instr::ConvertIntToJSValue; break;
1114 case IR::NumberType: opcode = V4Instr::ConvertNumberToJSValue; break;
1115 case IR::UrlType: opcode = V4Instr::ConvertUrlToJSValue; break;
1116 case IR::ColorType: opcode = V4Instr::ConvertColorToJSValue; break;
1117 case IR::StringType: opcode = V4Instr::ConvertStringToJSValue; break;
1118 case IR::ObjectType: opcode = V4Instr::ConvertObjectToJSValue; break;
1119 case IR::VarType: opcode = V4Instr::ConvertVarToJSValue; break;
1120 case IR::NullType: opcode = V4Instr::ConvertNullToJSValue; break;
1125 if (opcode != V4Instr::Noop) {
1127 conv.unaryop.output = dest;
1128 conv.unaryop.src = src;
1131 if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) {
1133 resolveUrl.unaryop.output = dest;
1134 resolveUrl.unaryop.src = dest;
1135 gen(V4Instr::ResolveUrl, resolveUrl);
1141 traceExpression(s->source, dest);
1145 void QV4CompilerPrivate::visitJump(IR::Jump *s)
1147 patches.append(Patch(s->target, bytecode.size()));
1150 i.offset = 0; // ### backpatch
1154 void QV4CompilerPrivate::visitCJump(IR::CJump *s)
1156 traceExpression(s->cond, currentReg);
1158 patches.append(Patch(s->iftrue, bytecode.size()));
1160 Instr::BranchTrue i;
1162 i.offset = 0; // ### backpatch
1166 void QV4CompilerPrivate::visitRet(IR::Ret *s)
1168 Q_ASSERT(s->expr != 0);
1170 int storeReg = currentReg;
1172 if (IR::Temp *temp = s->expr->asTemp()) {
1173 storeReg = temp->index;
1175 traceExpression(s->expr, storeReg);
1178 if (qmlBindingsTest) {
1179 Instr::TestV4Store test;
1180 test.reg = storeReg;
1182 case IR::StringType:
1183 test.regType = QMetaType::QString;
1186 test.regType = QMetaType::QUrl;
1189 test.regType = QMetaType::QColor;
1191 case IR::SGAnchorLineType:
1192 test.regType = QQmlMetaType::QQuickAnchorLineMetaTypeId();
1194 case IR::ObjectType:
1195 test.regType = QMetaType::QObjectStar;
1197 case IR::VariantType:
1198 test.regType = QMetaType::QVariant;
1201 test.regType = qMetaTypeId<v8::Handle<v8::Value> >();
1203 case IR::JSValueType:
1204 test.regType = qMetaTypeId<QJSValue>();
1207 test.regType = QMetaType::Bool;
1210 test.regType = QMetaType::Int;
1213 case IR::NumberType:
1214 test.regType = QMetaType::Double;
1225 store.index = expression->property->index;
1226 store.reg = storeReg;
1227 store.valueType = s->type == IR::FloatType ? FloatType : 0;
1228 store.exceptionId = exceptionId(s->line, s->column);
1232 void QV4Compiler::dump(const QByteArray &programData)
1234 const QV4Program *program = (const QV4Program *)programData.constData();
1236 qWarning() << "Program.bindings:" << program->bindings;
1237 qWarning() << "Program.dataLength:" << program->dataLength;
1238 qWarning() << "Program.subscriptions:" << program->subscriptions;
1240 const int programSize = program->instructionCount;
1241 const char *start = program->instructions();
1242 const char *end = start + programSize;
1244 bc.dump(start, end);
1248 Clear the state associated with attempting to compile a specific binding.
1249 This does not clear the global "committed binding" states.
1251 void QV4CompilerPrivate::resetInstanceState()
1253 data = committed.data;
1254 exceptions = committed.exceptions;
1255 usedSubscriptionIds.clear();
1256 subscriptionIds.clear();
1257 subscriptionOffset = committed.subscriptionCount;
1262 invalidatable = false;
1266 Mark the last compile as successful, and add it to the "committed data"
1269 Returns the index for the committed binding.
1271 int QV4CompilerPrivate::commitCompile()
1273 int rv = committed.count();
1274 committed.offsets << committed.bytecode.count();
1275 committed.dependencies << usedSubscriptionIds;
1276 committed.bytecode.append(bytecode.constData(), bytecode.size());
1277 committed.data = data;
1278 committed.exceptions = exceptions;
1279 committed.subscriptionCount = subscriptionOffset + subscriptionIds.count();
1281 committed.subscriptions.append(subscriptionIds);
1285 bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node)
1287 resetInstanceState();
1289 if (expression->property->type == -1)
1292 AST::SourceLocation location;
1293 if (AST::ExpressionNode *astExpression = node->expressionCast()) {
1294 location = astExpression->firstSourceLocation();
1295 } else if (AST::Statement *astStatement = node->statementCast()) {
1296 if (AST::Block *block = AST::cast<AST::Block *>(astStatement))
1297 location = block->lbraceToken;
1298 else if (AST::IfStatement *ifStmt = AST::cast<AST::IfStatement *>(astStatement))
1299 location = ifStmt->ifToken;
1306 IR::Function thisFunction(&pool), *function = &thisFunction;
1308 QV4IRBuilder irBuilder(expression, engine);
1309 if (!irBuilder(function, node, &invalidatable))
1312 bool discarded = false;
1313 qSwap(_discarded, discarded);
1314 qSwap(_function, function);
1315 trace(location.startLine, location.startColumn);
1316 qSwap(_function, function);
1317 qSwap(_discarded, discarded);
1319 if (qmlVerboseCompiler()) {
1320 QTextStream qerr(stderr, QIODevice::WriteOnly);
1322 qerr << "======== TODO ====== " << endl;
1324 qerr << "==================== " << endl;
1325 qerr << "\tline: " << location.startLine
1326 << "\tcolumn: " << location.startColumn
1328 foreach (IR::BasicBlock *bb, function->basicBlocks)
1333 if (discarded || subscriptionIds.count() > 0xFFFF || registerCount > 31)
1340 int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str)
1342 // ### string cleanup
1344 QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
1345 int offset = data.count();
1348 Instr::LoadString string;
1350 string.offset = offset;
1351 string.length = str.length();
1358 Returns true if the current expression has not already subscribed to \a sub in currentBlockMask.
1360 bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub)
1362 QString str = sub.join(QLatin1String("."));
1364 int *iter = subscriptionIds.value(str);
1368 quint32 *uiter = usedSubscriptionIds.value(*iter);
1372 return !(*uiter & currentBlockMask);
1375 int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub)
1377 QString str = sub.join(QLatin1String("."));
1378 int *iter = subscriptionIds.value(str);
1380 int count = subscriptionOffset + subscriptionIds.count();
1381 iter = &subscriptionIds[str];
1384 quint32 &u = usedSubscriptionIds[*iter];
1385 if (!(u & currentBlockMask)) {
1386 u |= currentBlockMask;
1387 usedSubscriptionIdsChanged = true;
1392 quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub)
1394 QString str = sub.join(QLatin1String("."));
1396 int *iter = subscriptionIds.value(str);
1397 Q_ASSERT(iter != 0);
1399 quint32 *uiter = usedSubscriptionIds.value(*iter);
1400 Q_ASSERT(uiter != 0);
1405 quint8 QV4CompilerPrivate::exceptionId(quint16 line, quint16 column)
1408 if (exceptions.count() < 0xFF) {
1409 rv = (quint8)exceptions.count();
1413 exceptions.append(e);
1418 quint8 QV4CompilerPrivate::exceptionId(QQmlJS::AST::ExpressionNode *n)
1421 if (n && exceptions.count() < 0xFF) {
1422 QQmlJS::AST::SourceLocation l = n->firstSourceLocation();
1423 rv = exceptionId(l.startLine, l.startColumn);
1428 QV4Compiler::QV4Compiler()
1429 : d(new QV4CompilerPrivate)
1431 qmlBindingsTest |= qmlBindingsTestEnv();
1434 QV4Compiler::~QV4Compiler()
1440 Returns true if any bindings were compiled.
1442 bool QV4Compiler::isValid() const
1444 return !d->committed.bytecode.isEmpty();
1448 -1 on failure, otherwise the binding index to use.
1450 int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine, bool *invalidatable)
1452 if (!expression.expression.asAST()) return false;
1454 if (qmlDisableOptimizer() || !qmlEnableV4)
1457 d->expression = &expression;
1460 if (d->compile(expression.expression.asAST())) {
1461 *invalidatable = d->isInvalidatable();
1462 return d->commitCompile();
1468 QByteArray QV4CompilerPrivate::buildSignalTable() const
1470 QHash<int, QList<QPair<int, quint32> > > table;
1472 for (int ii = 0; ii < committed.count(); ++ii) {
1473 const QQmlAssociationList<int, quint32> &deps = committed.dependencies.at(ii);
1474 for (QQmlAssociationList<int, quint32>::const_iterator iter = deps.begin(); iter != deps.end(); ++iter)
1475 table[iter->first].append(qMakePair(ii, iter->second));
1478 QVector<quint32> header;
1479 QVector<quint32> data;
1480 for (int ii = 0; ii < committed.subscriptionCount; ++ii) {
1481 header.append(committed.subscriptionCount + data.count());
1482 const QList<QPair<int, quint32> > &bindings = table[ii];
1483 data.append(bindings.count());
1484 for (int jj = 0; jj < bindings.count(); ++jj) {
1485 data.append(bindings.at(jj).first);
1486 data.append(bindings.at(jj).second);
1491 return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32));
1494 QByteArray QV4CompilerPrivate::buildExceptionData() const
1497 rv.resize(committed.exceptions.count() * sizeof(quint32));
1498 ::memcpy(rv.data(), committed.exceptions.constData(), rv.size());
1503 Returns the compiled program.
1505 QByteArray QV4Compiler::program() const
1507 QByteArray programData;
1511 prog.bindings = d->committed.count();
1514 QV4CompilerPrivate::Instr::Jump jump;
1517 for (int ii = 0; ii < d->committed.count(); ++ii) {
1518 jump.count = d->committed.count() - ii - 1;
1519 jump.count*= V4InstrMeta<V4Instr::Jump>::Size;
1520 jump.count+= d->committed.offsets.at(ii);
1525 QByteArray bytecode;
1526 bytecode.reserve(bc.size() + d->committed.bytecode.size());
1527 bytecode.append(bc.constData(), bc.size());
1528 bytecode.append(d->committed.bytecode.constData(), d->committed.bytecode.size());
1530 QByteArray data = d->committed.data;
1531 while (data.count() % 4) data.append('\0');
1532 prog.signalTableOffset = data.count();
1533 data += d->buildSignalTable();
1534 while (data.count() % 4) data.append('\0');
1535 prog.exceptionDataOffset = data.count();
1536 data += d->buildExceptionData();
1538 prog.dataLength = 4 * ((data.size() + 3) / 4);
1539 prog.subscriptions = d->committed.subscriptionCount;
1540 prog.instructionCount = bytecode.count();
1541 int size = sizeof(QV4Program) + bytecode.count();
1542 size += prog.dataLength;
1544 programData.resize(size);
1545 memcpy(programData.data(), &prog, sizeof(QV4Program));
1546 if (prog.dataLength)
1547 memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(),
1549 memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(),
1553 if (bindingsDump()) {
1554 qWarning().nospace() << "Subscription slots:";
1556 QQmlAssociationList<QString, int> subscriptionIds;
1557 foreach (subscriptionIds, d->committed.subscriptions) {
1558 for (QQmlAssociationList<QString, int>::ConstIterator iter = subscriptionIds.begin();
1559 iter != subscriptionIds.end(); ++iter) {
1560 qWarning().nospace() << " " << iter->first << "\t-> " << iter->second;
1563 QV4Compiler::dump(programData);
1569 void QV4Compiler::enableBindingsTest(bool e)
1572 qmlBindingsTest = true;
1574 qmlBindingsTest = qmlBindingsTestEnv();
1577 void QV4Compiler::enableV4(bool e)