1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
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>
55 DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP)
56 DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER)
57 DEFINE_BOOL_CONFIG_OPTION(qmlExperimental, QML_EXPERIMENTAL)
58 DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER)
59 DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST)
61 static bool qmlBindingsTest = false;
62 static bool qmlEnableV4 = true;
64 using namespace QQmlJS;
65 QV4CompilerPrivate::QV4CompilerPrivate()
66 : _function(0) , _block(0) , _discarded(false), registerCount(0)
73 void QV4CompilerPrivate::trace(int line, int column)
77 this->currentReg = _function->tempCount;
78 this->registerCount = qMax(this->registerCount, this->currentReg);
80 foreach (IR::BasicBlock *bb, _function->basicBlocks) {
81 if (! bb->isTerminated() && (bb->index + 1) < _function->basicBlocks.size())
82 bb->JUMP(_function->basicBlocks.at(bb->index + 1));
85 QVector<IR::BasicBlock *> blocks;
87 currentBlockMask = 0x00000001;
90 for (int i = 0; !_discarded && i < blocks.size(); ++i) {
91 IR::BasicBlock *block = blocks.at(i);
92 IR::BasicBlock *next = i + 1 < blocks.size() ? blocks.at(i + 1) : 0;
93 if (IR::Stmt *terminator = block->terminator()) {
94 if (IR::CJump *cj = terminator->asCJump()) {
95 if (cj->iffalse != next) {
96 IR::Jump *jump = _function->pool->New<IR::Jump>();
97 jump->init(cj->iffalse);
98 block->statements.append(jump);
100 } else if (IR::Jump *j = terminator->asJump()) {
101 if (j->target == next) {
102 block->statements.resize(block->statements.size() - 1);
107 block->offset = bytecode.size();
109 if (bytecode.isEmpty()) {
110 if (qmlBindingsTest || bindingsDump()) {
117 if (qmlBindingsTest) {
118 QString str = expression->expression.asScript();
119 QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
120 int offset = data.count();
123 Instr::EnableV4Test test;
125 test.offset = offset;
126 test.length = str.length();
132 int patchesCount = patches.count();
133 qSwap(usedSubscriptionIdsChanged, usic);
135 int blockopIndex = bytecode.size();
136 Instr::Block blockop;
137 blockop.block = currentBlockMask;
140 foreach (IR::Stmt *s, block->statements) {
145 qSwap(usedSubscriptionIdsChanged, usic);
148 if (currentBlockMask == 0x80000000) {
152 currentBlockMask <<= 1;
153 } else if (! _discarded) {
154 const int adjust = bytecode.remove(blockopIndex);
156 for (int ii = patchesCount; ii < patches.count(); ++ii)
157 patches[ii].offset -= adjust;
161 #ifdef DEBUG_IR_STRUCTURE
163 for (int i = 0; i < blocks.size(); ++i) {
164 dump.basicblock(blocks.at(i));
171 foreach (const Patch &patch, patches) {
172 V4Instr &instr = bytecode[patch.offset];
173 int size = V4Instr::size(instructionType(&instr));
174 instr.branchop.offset = patch.block->offset - patch.offset - size;
181 void QV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks)
183 for (int i = 0; i < _function->basicBlocks.size(); ++i) {
184 IR::BasicBlock *block = _function->basicBlocks.at(i);
186 while (! blocks->contains(block)) {
187 blocks->append(block);
189 if (IR::Stmt *terminator = block->terminator()) {
190 if (IR::CJump *cj = terminator->asCJump())
192 else if (IR::Jump *j = terminator->asJump())
199 void QV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r)
204 qSwap(currentReg, r);
206 qSwap(currentReg, r);
213 void QV4CompilerPrivate::visitConst(IR::Const *e)
238 if (qmlVerboseCompiler())
239 qWarning() << Q_FUNC_INFO << "unexpected type";
244 void QV4CompilerPrivate::visitString(IR::String *e)
246 registerLiteralString(currentReg, e->value);
249 void QV4CompilerPrivate::visitName(IR::Name *e)
252 // fetch the object and store it in reg.
253 traceExpression(e->base, currentReg);
255 _subscribeName.clear();
258 if (e->storage == IR::Name::RootStorage) {
260 Instr::LoadRoot instr;
261 instr.reg = currentReg;
264 if (e->symbol == IR::Name::IdObject) {
265 // The ID is a reference to the root object
269 } else if (e->storage == IR::Name::ScopeStorage) {
271 Instr::LoadScope instr;
272 instr.reg = currentReg;
275 _subscribeName << contextName();
277 } else if (e->storage == IR::Name::IdStorage) {
280 instr.reg = currentReg;
281 instr.index = e->idObject->idIndex;
284 _subscribeName << QLatin1String("$$$ID_") + *e->id;
286 if (blockNeedsSubscription(_subscribeName)) {
287 Instr::SubscribeId sub;
288 sub.reg = currentReg;
289 sub.offset = subscriptionIndex(_subscribeName);
290 sub.index = instr.index;
300 case IR::Name::Unbound:
301 case IR::Name::IdObject:
303 case IR::Name::Object: {
304 Q_ASSERT(!"Unreachable");
308 case IR::Name::AttachType: {
309 _subscribeName << *e->id;
311 Instr::LoadAttached attached;
312 attached.output = currentReg;
313 attached.reg = currentReg;
314 attached.exceptionId = exceptionId(e->line, e->column);
315 if (e->declarativeType->attachedPropertiesId() == -1)
317 attached.id = e->declarativeType->attachedPropertiesId();
321 case IR::Name::Property: {
322 _subscribeName << *e->id;
324 if (e->property->coreIndex == -1) {
326 e->property->load(prop, QQmlEnginePrivate::get(engine));
329 const int propTy = e->property->propType;
330 QQmlRegisterType regType;
333 case QMetaType::QReal:
336 case QMetaType::Bool:
342 case QMetaType::QString:
343 regType = QStringType;
345 case QMetaType::QUrl:
348 case QMetaType::QColor:
349 regType = QColorType;
353 if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
354 regType = PODValueType;
355 } else if (QQmlMetaType::isQObject(propTy)) {
356 regType = QObjectStarType;
358 if (qmlVerboseCompiler())
359 qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy);
360 discard(); // Unsupported type
367 if (e->property->hasAccessors()) {
368 Instr::FetchAndSubscribe fetch;
369 fetch.reg = currentReg;
370 fetch.subscription = subscriptionIndex(_subscribeName);
371 fetch.exceptionId = exceptionId(e->line, e->column);
372 fetch.valueType = regType;
373 fetch.property = *e->property;
376 if (blockNeedsSubscription(_subscribeName) && e->property->notifyIndex != -1) {
377 Instr::Subscribe sub;
378 sub.reg = currentReg;
379 sub.offset = subscriptionIndex(_subscribeName);
380 sub.index = e->property->notifyIndex;
385 fetch.reg = currentReg;
386 fetch.index = e->property->coreIndex;
387 fetch.exceptionId = exceptionId(e->line, e->column);
388 fetch.valueType = regType;
396 void QV4CompilerPrivate::visitTemp(IR::Temp *e)
398 if (currentReg != e->index) {
406 void QV4CompilerPrivate::visitUnop(IR::Unop *e)
408 quint8 src = currentReg;
410 if (IR::Temp *temp = e->expr->asTemp()) {
413 traceExpression(e->expr, src);
418 Q_ASSERT(!"unreachable");
422 convertToBool(e->expr, src);
423 if (src != currentReg) {
433 convertToBool(e->expr, src);
434 i.output = currentReg;
440 if (e->expr->type == IR::RealType) {
441 Instr::UnaryMinusReal i;
442 i.output = currentReg;
445 } else if (e->expr->type == IR::IntType) {
446 convertToReal(e->expr, currentReg);
447 Instr::UnaryMinusReal i;
448 i.output = currentReg;
457 if (e->expr->type == IR::RealType) {
458 Instr::UnaryPlusReal i;
459 i.output = currentReg;
462 } else if (e->expr->type == IR::IntType) {
463 convertToReal(e->expr, currentReg);
464 Instr::UnaryPlusReal i;
465 i.output = currentReg;
495 case IR::OpStrictEqual:
496 case IR::OpStrictNotEqual:
499 Q_ASSERT(!"unreachable");
504 void QV4CompilerPrivate::convertToReal(IR::Expr *expr, int reg)
506 if (expr->type == IR::RealType)
509 switch (expr->type) {
511 Instr::ConvertBoolToReal i;
512 i.output = i.src = reg;
517 Instr::ConvertIntToReal i;
518 i.output = i.src = reg;
532 void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg)
534 if (expr->type == IR::IntType)
537 switch (expr->type) {
539 Instr::ConvertBoolToInt i;
540 i.output = i.src = reg;
549 Instr::ConvertRealToInt i;
550 i.output = i.src = reg;
560 void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg)
562 if (expr->type == IR::BoolType)
565 switch (expr->type) {
571 Instr::ConvertIntToBool i;
572 i.output = i.src = reg;
577 Instr::ConvertRealToBool i;
578 i.output = i.src = reg;
582 case IR::StringType: {
583 Instr::ConvertStringToBool i;
584 i.output = i.src = reg;
588 case IR::ColorType: {
589 Instr::ConvertColorToBool i;
590 i.output = i.src = reg;
600 quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e)
604 return V4Instr::Noop;
611 return V4Instr::Noop;
614 return V4Instr::BitAndInt;
617 return V4Instr::BitOrInt;
620 return V4Instr::BitXorInt;
623 if (e->type == IR::StringType)
624 return V4Instr::AddString;
625 return V4Instr::AddReal;
628 return V4Instr::SubReal;
631 return V4Instr::MulReal;
634 return V4Instr::DivReal;
637 return V4Instr::ModReal;
640 return V4Instr::LShiftInt;
643 return V4Instr::RShiftInt;
646 return V4Instr::URShiftInt;
649 if (e->left->type == IR::StringType)
650 return V4Instr::GtString;
651 return V4Instr::GtReal;
654 if (e->left->type == IR::StringType)
655 return V4Instr::LtString;
656 return V4Instr::LtReal;
659 if (e->left->type == IR::StringType)
660 return V4Instr::GeString;
661 return V4Instr::GeReal;
664 if (e->left->type == IR::StringType)
665 return V4Instr::LeString;
666 return V4Instr::LeReal;
669 if (e->left->type == IR::StringType)
670 return V4Instr::EqualString;
671 return V4Instr::EqualReal;
674 if (e->left->type == IR::StringType)
675 return V4Instr::NotEqualString;
676 return V4Instr::NotEqualReal;
678 case IR::OpStrictEqual:
679 if (e->left->type == IR::StringType)
680 return V4Instr::StrictEqualString;
681 return V4Instr::StrictEqualReal;
683 case IR::OpStrictNotEqual:
684 if (e->left->type == IR::StringType)
685 return V4Instr::StrictNotEqualString;
686 return V4Instr::StrictNotEqualReal;
690 return V4Instr::Noop;
694 return V4Instr::Noop;
697 void QV4CompilerPrivate::visitBinop(IR::Binop *e)
699 int left = currentReg;
700 int right = currentReg + 1;
702 if (e->left->asTemp() && e->type != IR::StringType) // Not sure if the e->type != String test is needed
703 left = e->left->asTemp()->index;
705 traceExpression(e->left, left);
707 if (IR::Temp *t = e->right->asTemp())
710 traceExpression(e->right, right);
712 if (e->left->type != e->right->type) {
713 if (qmlVerboseCompiler())
714 qWarning().nospace() << "invalid operands to binary operator " << IR::binaryOperator(e->op)
715 << "(`" << IR::binaryOperator(e->left->type)
717 << IR::binaryOperator(e->right->type)
743 convertToInt(e->left, left);
744 convertToInt(e->right, right);
748 if (e->type != IR::StringType) {
749 convertToReal(e->left, left);
750 convertToReal(e->right, right);
758 convertToReal(e->left, left);
759 convertToReal(e->right, right);
768 case IR::OpStrictEqual:
769 case IR::OpStrictNotEqual:
770 if (e->left->type != IR::StringType) {
771 convertToReal(e->left, left);
772 convertToReal(e->right, right);
778 discard(); // ### unreachable
782 const quint8 opcode = instructionOpcode(e);
783 if (opcode != V4Instr::Noop) {
785 instr.binaryop.output = currentReg;
786 instr.binaryop.left = left;
787 instr.binaryop.right = right;
788 gen(static_cast<V4Instr::Type>(opcode), instr);
792 void QV4CompilerPrivate::visitCall(IR::Call *call)
794 if (IR::Name *name = call->base->asName()) {
795 IR::Expr *arg = call->onlyArgument();
796 if (arg != 0 && arg->type == IR::RealType) {
797 traceExpression(arg, currentReg);
799 switch (name->builtin) {
800 case IR::NoBuiltinSymbol:
803 case IR::MathSinBultinFunction: {
804 Instr::MathSinReal i;
805 i.output = i.src = currentReg;
809 case IR::MathCosBultinFunction: {
810 Instr::MathCosReal i;
811 i.output = i.src = currentReg;
815 case IR::MathRoundBultinFunction: {
816 Instr::MathRoundReal i;
817 i.output = i.src = currentReg;
821 case IR::MathFloorBultinFunction: {
822 Instr::MathFloorReal i;
823 i.output = i.src = currentReg;
827 case IR::MathPIBuiltinConstant:
833 if (qmlVerboseCompiler())
834 qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__;
842 void QV4CompilerPrivate::visitExp(IR::Exp *s)
844 traceExpression(s->expr, currentReg);
847 void QV4CompilerPrivate::visitMove(IR::Move *s)
849 IR::Temp *target = s->target->asTemp();
850 Q_ASSERT(target != 0);
852 quint8 dest = target->index;
854 if (target->type != s->source->type) {
857 if (IR::Temp *t = s->source->asTemp())
860 traceExpression(s->source, dest);
862 V4Instr::Type opcode = V4Instr::Noop;
863 IR::Type targetTy = s->target->type;
864 IR::Type sourceTy = s->source->type;
866 if (sourceTy == IR::UrlType) {
870 // nothing to do. V4 will generate optimized
871 // url-to-xxx conversions.
874 // generate a UrlToString conversion and fix
875 // the type of the source expression.
877 conv.unaryop.output = V4Instr::ConvertUrlToString;
878 conv.unaryop.src = src;
881 sourceTy = IR::StringType;
887 if (targetTy == IR::BoolType) {
889 case IR::IntType: opcode = V4Instr::ConvertIntToBool; break;
890 case IR::RealType: opcode = V4Instr::ConvertRealToBool; break;
891 case IR::StringType: opcode = V4Instr::ConvertStringToBool; break;
892 case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break;
893 case IR::ColorType: opcode = V4Instr::ConvertColorToBool; break;
896 } else if (targetTy == IR::IntType) {
898 case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break;
900 if (s->isMoveForReturn)
901 opcode = V4Instr::MathRoundReal;
903 opcode = V4Instr::ConvertRealToInt;
906 case IR::StringType: opcode = V4Instr::ConvertStringToInt; break;
909 } else if (targetTy == IR::RealType) {
911 case IR::BoolType: opcode = V4Instr::ConvertBoolToReal; break;
912 case IR::IntType: opcode = V4Instr::ConvertIntToReal; break;
913 case IR::StringType: opcode = V4Instr::ConvertStringToReal; break;
916 } else if (targetTy == IR::StringType) {
918 case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break;
919 case IR::IntType: opcode = V4Instr::ConvertIntToString; break;
920 case IR::RealType: opcode = V4Instr::ConvertRealToString; break;
921 case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break;
922 case IR::ColorType: opcode = V4Instr::ConvertColorToString; break;
925 } else if (targetTy == IR::UrlType) {
926 V4Instr convToString;
927 convToString.unaryop.output = dest;
928 convToString.unaryop.src = src;
930 // try to convert the source expression to a string.
932 case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break;
933 case IR::IntType: gen(V4Instr::ConvertIntToString, convToString); sourceTy = IR::StringType; break;
934 case IR::RealType: gen(V4Instr::ConvertRealToString, convToString); sourceTy = IR::StringType; break;
935 case IR::ColorType: gen(V4Instr::ConvertColorToString, convToString); sourceTy = IR::StringType; break;
939 if (sourceTy == IR::StringType)
940 opcode = V4Instr::ConvertStringToUrl;
941 } else if (targetTy == IR::ColorType) {
943 case IR::StringType: opcode = V4Instr::ConvertStringToColor; break;
947 if (opcode != V4Instr::Noop) {
949 conv.unaryop.output = dest;
950 conv.unaryop.src = src;
953 if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) {
955 resolveUrl.unaryop.output = dest;
956 resolveUrl.unaryop.src = dest;
957 gen(V4Instr::ResolveUrl, resolveUrl);
963 traceExpression(s->source, dest);
967 void QV4CompilerPrivate::visitJump(IR::Jump *s)
969 patches.append(Patch(s->target, bytecode.size()));
972 i.offset = 0; // ### backpatch
976 void QV4CompilerPrivate::visitCJump(IR::CJump *s)
978 traceExpression(s->cond, currentReg);
980 patches.append(Patch(s->iftrue, bytecode.size()));
984 i.offset = 0; // ### backpatch
988 void QV4CompilerPrivate::visitRet(IR::Ret *s)
990 Q_ASSERT(s->expr != 0);
992 int storeReg = currentReg;
994 if (IR::Temp *temp = s->expr->asTemp()) {
995 storeReg = temp->index;
997 traceExpression(s->expr, storeReg);
1000 if (qmlBindingsTest) {
1001 Instr::TestV4Store test;
1002 test.reg = storeReg;
1004 case IR::StringType:
1005 test.regType = QMetaType::QString;
1008 test.regType = QMetaType::QUrl;
1011 test.regType = QMetaType::QColor;
1013 case IR::SGAnchorLineType:
1014 test.regType = QQmlMetaType::QQuickAnchorLineMetaTypeId();
1016 case IR::ObjectType:
1017 test.regType = QMetaType::QObjectStar;
1020 test.regType = QMetaType::Bool;
1023 test.regType = QMetaType::Int;
1026 test.regType = QMetaType::QReal;
1037 store.index = expression->property->index;
1038 store.reg = storeReg;
1039 store.exceptionId = exceptionId(s->line, s->column);
1043 void QV4Compiler::dump(const QByteArray &programData)
1045 const QV4Program *program = (const QV4Program *)programData.constData();
1047 qWarning() << "Program.bindings:" << program->bindings;
1048 qWarning() << "Program.dataLength:" << program->dataLength;
1049 qWarning() << "Program.subscriptions:" << program->subscriptions;
1050 qWarning() << "Program.indentifiers:" << program->identifiers;
1052 const int programSize = program->instructionCount;
1053 const char *start = program->instructions();
1054 const char *end = start + programSize;
1056 bc.dump(start, end);
1060 Clear the state associated with attempting to compile a specific binding.
1061 This does not clear the global "committed binding" states.
1063 void QV4CompilerPrivate::resetInstanceState()
1065 data = committed.data;
1066 exceptions = committed.exceptions;
1067 usedSubscriptionIds.clear();
1068 subscriptionIds = committed.subscriptionIds;
1069 registeredStrings = committed.registeredStrings;
1077 Mark the last compile as successful, and add it to the "committed data"
1080 Returns the index for the committed binding.
1082 int QV4CompilerPrivate::commitCompile()
1084 int rv = committed.count();
1085 committed.offsets << committed.bytecode.count();
1086 committed.dependencies << usedSubscriptionIds;
1087 committed.bytecode.append(bytecode.constData(), bytecode.size());
1088 committed.data = data;
1089 committed.exceptions = exceptions;
1090 committed.subscriptionIds = subscriptionIds;
1091 committed.registeredStrings = registeredStrings;
1095 bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node)
1097 resetInstanceState();
1099 if (expression->property->type == -1)
1102 AST::SourceLocation location;
1103 if (AST::ExpressionNode *astExpression = node->expressionCast()) {
1104 location = astExpression->firstSourceLocation();
1105 } else if (AST::Statement *astStatement = node->statementCast()) {
1106 if (AST::Block *block = AST::cast<AST::Block *>(astStatement))
1107 location = block->lbraceToken;
1108 else if (AST::IfStatement *ifStmt = AST::cast<AST::IfStatement *>(astStatement))
1109 location = ifStmt->ifToken;
1116 IR::Function thisFunction(&pool), *function = &thisFunction;
1118 QV4IRBuilder irBuilder(expression, engine);
1119 if (!irBuilder(function, node))
1122 bool discarded = false;
1123 qSwap(_discarded, discarded);
1124 qSwap(_function, function);
1125 trace(location.startLine, location.startColumn);
1126 qSwap(_function, function);
1127 qSwap(_discarded, discarded);
1129 if (qmlVerboseCompiler()) {
1130 QTextStream qerr(stderr, QIODevice::WriteOnly);
1132 qerr << "======== TODO ====== " << endl;
1134 qerr << "==================== " << endl;
1135 qerr << "\tline: " << location.startLine
1136 << "\tcolumn: " << location.startColumn
1138 foreach (IR::BasicBlock *bb, function->basicBlocks)
1143 if (discarded || subscriptionIds.count() > 0xFFFF || registeredStrings.count() > 0xFFFF || registerCount > 31)
1150 int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str)
1152 // ### string cleanup
1154 QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
1155 int offset = data.count();
1158 Instr::LoadString string;
1160 string.offset = offset;
1161 string.length = str.length();
1167 // Returns an identifier offset
1168 int QV4CompilerPrivate::registerString(const QString &string)
1170 Q_ASSERT(!string.isEmpty());
1172 QPair<int, int> *iter = registeredStrings.value(string);
1175 quint32 len = string.length();
1176 QByteArray lendata((const char *)&len, sizeof(quint32));
1177 QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar));
1178 strdata.prepend(lendata);
1179 int rv = data.count();
1182 iter = ®isteredStrings[string];
1183 *iter = qMakePair(registeredStrings.count(), rv);
1186 Instr::InitString reg;
1187 reg.offset = iter->first;
1188 reg.dataIdx = iter->second;
1194 Returns true if the current expression has not already subscribed to \a sub in currentBlockMask.
1196 bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub)
1198 QString str = sub.join(QLatin1String("."));
1200 int *iter = subscriptionIds.value(str);
1204 quint32 *uiter = usedSubscriptionIds.value(*iter);
1208 return !(*uiter & currentBlockMask);
1211 int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub)
1213 QString str = sub.join(QLatin1String("."));
1214 int *iter = subscriptionIds.value(str);
1216 int count = subscriptionIds.count();
1217 iter = &subscriptionIds[str];
1220 quint32 &u = usedSubscriptionIds[*iter];
1221 if (!(u & currentBlockMask)) {
1222 u |= currentBlockMask;
1223 usedSubscriptionIdsChanged = true;
1228 quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub)
1230 QString str = sub.join(QLatin1String("."));
1232 int *iter = subscriptionIds.value(str);
1233 Q_ASSERT(iter != 0);
1235 quint32 *uiter = usedSubscriptionIds.value(*iter);
1236 Q_ASSERT(uiter != 0);
1241 quint8 QV4CompilerPrivate::exceptionId(quint32 line, quint32 column)
1244 if (exceptions.count() < 0xFF) {
1245 rv = (quint8)exceptions.count();
1249 exceptions.append(e);
1254 quint8 QV4CompilerPrivate::exceptionId(QQmlJS::AST::ExpressionNode *n)
1257 if (n && exceptions.count() < 0xFF) {
1258 QQmlJS::AST::SourceLocation l = n->firstSourceLocation();
1259 rv = exceptionId(l.startLine, l.startColumn);
1264 QV4Compiler::QV4Compiler()
1265 : d(new QV4CompilerPrivate)
1267 qmlBindingsTest |= qmlBindingsTestEnv();
1270 QV4Compiler::~QV4Compiler()
1276 Returns true if any bindings were compiled.
1278 bool QV4Compiler::isValid() const
1280 return !d->committed.bytecode.isEmpty();
1284 -1 on failure, otherwise the binding index to use.
1286 int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine)
1288 if (!expression.expression.asAST()) return false;
1290 if (!qmlExperimental() && expression.property->isValueTypeSubProperty)
1293 if (qmlDisableOptimizer() || !qmlEnableV4)
1296 d->expression = &expression;
1299 if (d->compile(expression.expression.asAST())) {
1300 return d->commitCompile();
1306 QByteArray QV4CompilerPrivate::buildSignalTable() const
1308 QHash<int, QList<QPair<int, quint32> > > table;
1310 for (int ii = 0; ii < committed.count(); ++ii) {
1311 const QQmlAssociationList<int, quint32> &deps = committed.dependencies.at(ii);
1312 for (QQmlAssociationList<int, quint32>::const_iterator iter = deps.begin(); iter != deps.end(); ++iter)
1313 table[iter->first].append(qMakePair(ii, iter->second));
1316 QVector<quint32> header;
1317 QVector<quint32> data;
1318 for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) {
1319 header.append(committed.subscriptionIds.count() + data.count());
1320 const QList<QPair<int, quint32> > &bindings = table[ii];
1321 data.append(bindings.count());
1322 for (int jj = 0; jj < bindings.count(); ++jj) {
1323 data.append(bindings.at(jj).first);
1324 data.append(bindings.at(jj).second);
1329 return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32));
1332 QByteArray QV4CompilerPrivate::buildExceptionData() const
1335 rv.resize(committed.exceptions.count() * sizeof(quint64));
1336 ::memcpy(rv.data(), committed.exceptions.constData(), rv.size());
1341 Returns the compiled program.
1343 QByteArray QV4Compiler::program() const
1345 QByteArray programData;
1349 prog.bindings = d->committed.count();
1352 QV4CompilerPrivate::Instr::Jump jump;
1355 for (int ii = 0; ii < d->committed.count(); ++ii) {
1356 jump.count = d->committed.count() - ii - 1;
1357 jump.count*= V4InstrMeta<V4Instr::Jump>::Size;
1358 jump.count+= d->committed.offsets.at(ii);
1363 QByteArray bytecode;
1364 bytecode.reserve(bc.size() + d->committed.bytecode.size());
1365 bytecode.append(bc.constData(), bc.size());
1366 bytecode.append(d->committed.bytecode.constData(), d->committed.bytecode.size());
1368 QByteArray data = d->committed.data;
1369 while (data.count() % 4) data.append('\0');
1370 prog.signalTableOffset = data.count();
1371 data += d->buildSignalTable();
1372 while (data.count() % 4) data.append('\0');
1373 prog.exceptionDataOffset = data.count();
1374 data += d->buildExceptionData();
1376 prog.dataLength = 4 * ((data.size() + 3) / 4);
1377 prog.subscriptions = d->committed.subscriptionIds.count();
1378 prog.identifiers = d->committed.registeredStrings.count();
1379 prog.instructionCount = bytecode.count();
1380 int size = sizeof(QV4Program) + bytecode.count();
1381 size += prog.dataLength;
1383 programData.resize(size);
1384 memcpy(programData.data(), &prog, sizeof(QV4Program));
1385 if (prog.dataLength)
1386 memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(),
1388 memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(),
1392 if (bindingsDump()) {
1393 qWarning().nospace() << "Subscription slots:";
1395 for (QQmlAssociationList<QString, int>::ConstIterator iter = d->committed.subscriptionIds.begin();
1396 iter != d->committed.subscriptionIds.end();
1398 qWarning().nospace() << " " << iter->first << "\t-> " << iter->second;
1401 QV4Compiler::dump(programData);
1407 void QV4Compiler::enableBindingsTest(bool e)
1410 qmlBindingsTest = true;
1412 qmlBindingsTest = qmlBindingsTestEnv();
1415 void QV4Compiler::enableV4(bool e)