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(qmlVerboseCompiler, QML_VERBOSE_COMPILER)
58 DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST)
60 static bool qmlBindingsTest = false;
61 static bool qmlEnableV4 = true;
63 using namespace QQmlJS;
64 QV4CompilerPrivate::QV4CompilerPrivate()
65 : _function(0) , _block(0) , _discarded(false), registerCount(0)
72 void QV4CompilerPrivate::trace(int line, int column)
76 this->currentReg = _function->tempCount;
77 this->registerCount = qMax(this->registerCount, this->currentReg);
79 foreach (IR::BasicBlock *bb, _function->basicBlocks) {
80 if (! bb->isTerminated() && (bb->index + 1) < _function->basicBlocks.size())
81 bb->JUMP(_function->basicBlocks.at(bb->index + 1));
84 QVector<IR::BasicBlock *> blocks;
86 currentBlockMask = 0x00000001;
89 for (int i = 0; !_discarded && i < blocks.size(); ++i) {
90 IR::BasicBlock *block = blocks.at(i);
91 IR::BasicBlock *next = i + 1 < blocks.size() ? blocks.at(i + 1) : 0;
92 if (IR::Stmt *terminator = block->terminator()) {
93 if (IR::CJump *cj = terminator->asCJump()) {
94 if (cj->iffalse != next) {
95 IR::Jump *jump = _function->pool->New<IR::Jump>();
96 jump->init(cj->iffalse);
97 block->statements.append(jump);
99 } else if (IR::Jump *j = terminator->asJump()) {
100 if (j->target == next) {
101 block->statements.resize(block->statements.size() - 1);
106 block->offset = bytecode.size();
108 if (bytecode.isEmpty()) {
109 if (qmlBindingsTest || bindingsDump()) {
116 if (qmlBindingsTest) {
117 QString str = expression->expression.asScript();
118 QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
119 int offset = data.count();
122 Instr::EnableV4Test test;
124 test.offset = offset;
125 test.length = str.length();
131 int patchesCount = patches.count();
132 qSwap(usedSubscriptionIdsChanged, usic);
134 int blockopIndex = bytecode.size();
135 Instr::Block blockop;
136 blockop.block = currentBlockMask;
139 foreach (IR::Stmt *s, block->statements) {
144 qSwap(usedSubscriptionIdsChanged, usic);
147 if (currentBlockMask == 0x80000000) {
151 currentBlockMask <<= 1;
152 } else if (! _discarded) {
153 const int adjust = bytecode.remove(blockopIndex);
155 for (int ii = patchesCount; ii < patches.count(); ++ii)
156 patches[ii].offset -= adjust;
160 #ifdef DEBUG_IR_STRUCTURE
162 for (int i = 0; i < blocks.size(); ++i) {
163 dump.basicblock(blocks.at(i));
170 foreach (const Patch &patch, patches) {
171 V4Instr &instr = bytecode[patch.offset];
172 int size = V4Instr::size(instructionType(&instr));
173 instr.branchop.offset = patch.block->offset - patch.offset - size;
180 void QV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks)
182 for (int i = 0; i < _function->basicBlocks.size(); ++i) {
183 IR::BasicBlock *block = _function->basicBlocks.at(i);
185 while (! blocks->contains(block)) {
186 blocks->append(block);
188 if (IR::Stmt *terminator = block->terminator()) {
189 if (IR::CJump *cj = terminator->asCJump())
191 else if (IR::Jump *j = terminator->asJump())
198 void QV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r)
203 qSwap(currentReg, r);
205 qSwap(currentReg, r);
212 void QV4CompilerPrivate::visitConst(IR::Const *e)
243 if (qmlVerboseCompiler())
244 qWarning() << Q_FUNC_INFO << "unexpected type";
249 void QV4CompilerPrivate::visitString(IR::String *e)
251 registerLiteralString(currentReg, e->value);
254 void QV4CompilerPrivate::visitName(IR::Name *e)
257 // fetch the object and store it in reg.
258 traceExpression(e->base, currentReg);
260 _subscribeName.clear();
263 if (e->storage == IR::Name::RootStorage) {
265 Instr::LoadRoot instr;
266 instr.reg = currentReg;
269 if (e->symbol == IR::Name::IdObject) {
270 // The ID is a reference to the root object
274 } else if (e->storage == IR::Name::ScopeStorage) {
276 Instr::LoadScope instr;
277 instr.reg = currentReg;
280 _subscribeName << contextName();
282 } else if (e->storage == IR::Name::IdStorage) {
285 instr.reg = currentReg;
286 instr.index = e->idObject->idIndex;
289 _subscribeName << QLatin1String("$$$ID_") + *e->id;
291 if (blockNeedsSubscription(_subscribeName)) {
292 Instr::SubscribeId sub;
293 sub.reg = currentReg;
294 sub.offset = subscriptionIndex(_subscribeName);
295 sub.index = instr.index;
305 case IR::Name::Unbound:
306 case IR::Name::IdObject:
308 case IR::Name::Object: {
309 Q_ASSERT(!"Unreachable");
313 case IR::Name::AttachType: {
314 _subscribeName << *e->id;
316 Instr::LoadAttached attached;
317 attached.output = currentReg;
318 attached.reg = currentReg;
319 attached.exceptionId = exceptionId(e->line, e->column);
320 if (e->declarativeType->attachedPropertiesId() == -1)
322 attached.id = e->declarativeType->attachedPropertiesId();
326 case IR::Name::ModuleObject: {
328 Existing module object lookup methods include:
329 1. string -> module object (search via importCache->query(name))
330 2. QQmlMetaType::ModuleApi -> module object (via QQmlEnginePrivate::moduleApiInstance() cache)
331 We currently use 1, which is not ideal for performance
333 _subscribeName << *e->id;
335 registerLiteralString(currentReg, e->id);
337 Instr::LoadModuleObject module;
338 module.reg = currentReg;
342 case IR::Name::Property: {
343 _subscribeName << *e->id;
345 if (e->property->coreIndex == -1) {
347 e->property->load(prop, QQmlEnginePrivate::get(engine));
350 const int propTy = e->property->propType;
351 QQmlRegisterType regType;
354 case QMetaType::QReal:
357 case QMetaType::Bool:
363 case QMetaType::QString:
364 regType = QStringType;
366 case QMetaType::QUrl:
369 case QMetaType::QColor:
370 regType = QColorType;
374 if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
375 regType = PODValueType;
376 } else if (QQmlMetaType::isQObject(propTy)) {
377 regType = QObjectStarType;
379 if (qmlVerboseCompiler())
380 qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy);
381 discard(); // Unsupported type
388 if (e->property->hasAccessors()) {
389 Instr::FetchAndSubscribe fetch;
390 fetch.reg = currentReg;
391 fetch.subscription = subscriptionIndex(_subscribeName);
392 fetch.exceptionId = exceptionId(e->line, e->column);
393 fetch.valueType = regType;
394 fetch.property = *e->property;
397 if (blockNeedsSubscription(_subscribeName) && e->property->notifyIndex != -1) {
398 Instr::Subscribe sub;
399 sub.reg = currentReg;
400 sub.offset = subscriptionIndex(_subscribeName);
401 sub.index = e->property->notifyIndex;
406 fetch.reg = currentReg;
407 fetch.index = e->property->coreIndex;
408 fetch.exceptionId = exceptionId(e->line, e->column);
409 fetch.valueType = regType;
417 void QV4CompilerPrivate::visitTemp(IR::Temp *e)
419 if (currentReg != e->index) {
427 void QV4CompilerPrivate::visitUnop(IR::Unop *e)
429 quint8 src = currentReg;
431 if (IR::Temp *temp = e->expr->asTemp()) {
434 traceExpression(e->expr, src);
439 Q_ASSERT(!"unreachable");
443 convertToBool(e->expr, src);
444 if (src != currentReg) {
454 convertToBool(e->expr, src);
455 i.output = currentReg;
461 if (e->expr->type == IR::RealType) {
462 Instr::UnaryMinusReal i;
463 i.output = currentReg;
466 } else if (e->expr->type == IR::IntType) {
467 convertToReal(e->expr, currentReg);
468 Instr::UnaryMinusReal i;
469 i.output = currentReg;
478 if (e->expr->type == IR::RealType) {
479 Instr::UnaryPlusReal i;
480 i.output = currentReg;
483 } else if (e->expr->type == IR::IntType) {
484 convertToReal(e->expr, currentReg);
485 Instr::UnaryPlusReal i;
486 i.output = currentReg;
516 case IR::OpStrictEqual:
517 case IR::OpStrictNotEqual:
520 Q_ASSERT(!"unreachable");
525 void QV4CompilerPrivate::convertToReal(IR::Expr *expr, int reg)
527 if (expr->type == IR::RealType)
530 switch (expr->type) {
532 Instr::ConvertBoolToReal i;
533 i.output = i.src = reg;
538 Instr::ConvertIntToReal i;
539 i.output = i.src = reg;
553 void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg)
555 if (expr->type == IR::IntType)
558 switch (expr->type) {
560 Instr::ConvertBoolToInt i;
561 i.output = i.src = reg;
570 Instr::ConvertRealToInt i;
571 i.output = i.src = reg;
581 void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg)
583 if (expr->type == IR::BoolType)
586 switch (expr->type) {
592 Instr::ConvertIntToBool i;
593 i.output = i.src = reg;
598 Instr::ConvertRealToBool i;
599 i.output = i.src = reg;
603 case IR::StringType: {
604 Instr::ConvertStringToBool i;
605 i.output = i.src = reg;
609 case IR::ColorType: {
610 Instr::ConvertColorToBool i;
611 i.output = i.src = reg;
621 quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e)
625 return V4Instr::Noop;
632 return V4Instr::Noop;
635 return V4Instr::BitAndInt;
638 return V4Instr::BitOrInt;
641 return V4Instr::BitXorInt;
644 if (e->type == IR::StringType)
645 return V4Instr::AddString;
646 return V4Instr::AddReal;
649 return V4Instr::SubReal;
652 return V4Instr::MulReal;
655 return V4Instr::DivReal;
658 return V4Instr::ModReal;
661 return V4Instr::LShiftInt;
664 return V4Instr::RShiftInt;
667 return V4Instr::URShiftInt;
670 if (e->left->type == IR::StringType)
671 return V4Instr::GtString;
672 return V4Instr::GtReal;
675 if (e->left->type == IR::StringType)
676 return V4Instr::LtString;
677 return V4Instr::LtReal;
680 if (e->left->type == IR::StringType)
681 return V4Instr::GeString;
682 return V4Instr::GeReal;
685 if (e->left->type == IR::StringType)
686 return V4Instr::LeString;
687 return V4Instr::LeReal;
690 if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
691 return V4Instr::EqualObject;
692 if (e->left->type == IR::StringType)
693 return V4Instr::EqualString;
694 return V4Instr::EqualReal;
697 if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
698 return V4Instr::NotEqualObject;
699 if (e->left->type == IR::StringType)
700 return V4Instr::NotEqualString;
701 return V4Instr::NotEqualReal;
703 case IR::OpStrictEqual:
704 if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
705 return V4Instr::StrictEqualObject;
706 if (e->left->type == IR::StringType)
707 return V4Instr::StrictEqualString;
708 return V4Instr::StrictEqualReal;
710 case IR::OpStrictNotEqual:
711 if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
712 return V4Instr::StrictNotEqualObject;
713 if (e->left->type == IR::StringType)
714 return V4Instr::StrictNotEqualString;
715 return V4Instr::StrictNotEqualReal;
719 return V4Instr::Noop;
723 return V4Instr::Noop;
726 void QV4CompilerPrivate::visitBinop(IR::Binop *e)
728 if (e->type == IR::InvalidType) {
733 int left = currentReg;
734 int right = currentReg + 1;
736 traceExpression(e->left, left);
737 traceExpression(e->right, right);
739 // At this point it is possible that the type of the
740 // subexpressions is different. This can happen because
741 // we keep BINOP expressions in HIR.
763 convertToInt(e->left, left);
764 convertToInt(e->right, right);
768 if (e->type != IR::StringType) {
769 convertToReal(e->left, left);
770 convertToReal(e->right, right);
778 convertToReal(e->left, left);
779 convertToReal(e->right, right);
788 case IR::OpStrictEqual:
789 case IR::OpStrictNotEqual:
790 if (e->left->type >= IR::FirstNumberType) {
791 convertToReal(e->left, left);
792 convertToReal(e->right, right);
798 discard(); // ### unreachable
802 const quint8 opcode = instructionOpcode(e);
803 if (opcode != V4Instr::Noop) {
805 instr.binaryop.output = currentReg;
806 instr.binaryop.left = left;
807 instr.binaryop.right = right;
808 gen(static_cast<V4Instr::Type>(opcode), instr);
812 void QV4CompilerPrivate::visitCall(IR::Call *call)
814 if (IR::Name *name = call->base->asName()) {
815 IR::Expr *arg = call->onlyArgument();
816 if (arg != 0 && arg->type == IR::RealType) {
817 traceExpression(arg, currentReg);
819 switch (name->builtin) {
820 case IR::NoBuiltinSymbol:
823 case IR::MathSinBultinFunction: {
824 Instr::MathSinReal i;
825 i.output = i.src = currentReg;
829 case IR::MathCosBultinFunction: {
830 Instr::MathCosReal i;
831 i.output = i.src = currentReg;
835 case IR::MathAbsBuiltinFunction: {
836 Instr::MathAbsReal i;
837 i.output = i.src = currentReg;
841 case IR::MathRoundBultinFunction: {
842 Instr::MathRoundReal i;
843 i.output = i.src = currentReg;
847 case IR::MathFloorBultinFunction: {
848 Instr::MathFloorReal i;
849 i.output = i.src = currentReg;
853 case IR::MathCeilBuiltinFunction: {
854 Instr::MathCeilReal i;
855 i.output = i.src = currentReg;
859 case IR::MathPIBuiltinConstant:
864 if (name->builtin == IR::MathMaxBuiltinFunction ||
865 name->builtin == IR::MathMinBuiltinFunction) {
867 //only handles the most common case of exactly two arguments
868 if (call->args && call->args->next && !call->args->next->next) {
869 IR::Expr *arg1 = call->args->expr;
870 IR::Expr *arg2 = call->args->next->expr;
872 if (arg1 != 0 && arg1->type == IR::RealType &&
873 arg2 != 0 && arg2->type == IR::RealType) {
875 traceExpression(arg1, currentReg);
876 traceExpression(arg2, currentReg + 1);
878 if (name->builtin == IR::MathMaxBuiltinFunction) {
879 Instr::MathMaxReal i;
881 i.right = currentReg + 1;
882 i.output = currentReg;
885 } else if (name->builtin == IR::MathMinBuiltinFunction) {
886 Instr::MathMinReal i;
888 i.right = currentReg + 1;
889 i.output = currentReg;
899 if (qmlVerboseCompiler())
900 qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__;
908 void QV4CompilerPrivate::visitExp(IR::Exp *s)
910 traceExpression(s->expr, currentReg);
913 void QV4CompilerPrivate::visitMove(IR::Move *s)
915 IR::Temp *target = s->target->asTemp();
916 Q_ASSERT(target != 0);
918 quint8 dest = target->index;
920 if (target->type != s->source->type) {
923 if (IR::Temp *t = s->source->asTemp())
926 traceExpression(s->source, dest);
928 V4Instr::Type opcode = V4Instr::Noop;
929 IR::Type targetTy = s->target->type;
930 IR::Type sourceTy = s->source->type;
932 if (sourceTy == IR::UrlType) {
936 // nothing to do. V4 will generate optimized
937 // url-to-xxx conversions.
940 // generate a UrlToString conversion and fix
941 // the type of the source expression.
943 conv.unaryop.output = V4Instr::ConvertUrlToString;
944 conv.unaryop.src = src;
947 sourceTy = IR::StringType;
953 if (targetTy == IR::BoolType) {
955 case IR::IntType: opcode = V4Instr::ConvertIntToBool; break;
956 case IR::RealType: opcode = V4Instr::ConvertRealToBool; break;
957 case IR::StringType: opcode = V4Instr::ConvertStringToBool; break;
958 case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break;
959 case IR::ColorType: opcode = V4Instr::ConvertColorToBool; break;
960 case IR::ObjectType: opcode = V4Instr::ConvertObjectToBool; break;
963 } else if (targetTy == IR::IntType) {
965 case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break;
967 if (s->isMoveForReturn)
968 opcode = V4Instr::MathRoundReal;
970 opcode = V4Instr::ConvertRealToInt;
973 case IR::StringType: opcode = V4Instr::ConvertStringToInt; break;
976 } else if (targetTy == IR::RealType) {
978 case IR::BoolType: opcode = V4Instr::ConvertBoolToReal; break;
979 case IR::IntType: opcode = V4Instr::ConvertIntToReal; break;
980 case IR::StringType: opcode = V4Instr::ConvertStringToReal; break;
983 } else if (targetTy == IR::StringType) {
985 case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break;
986 case IR::IntType: opcode = V4Instr::ConvertIntToString; break;
987 case IR::RealType: opcode = V4Instr::ConvertRealToString; break;
988 case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break;
989 case IR::ColorType: opcode = V4Instr::ConvertColorToString; break;
992 } else if (targetTy == IR::UrlType) {
993 V4Instr convToString;
994 convToString.unaryop.output = dest;
995 convToString.unaryop.src = src;
997 // try to convert the source expression to a string.
999 case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break;
1000 case IR::IntType: gen(V4Instr::ConvertIntToString, convToString); sourceTy = IR::StringType; break;
1001 case IR::RealType: gen(V4Instr::ConvertRealToString, convToString); sourceTy = IR::StringType; break;
1002 case IR::ColorType: gen(V4Instr::ConvertColorToString, convToString); sourceTy = IR::StringType; break;
1006 if (sourceTy == IR::StringType)
1007 opcode = V4Instr::ConvertStringToUrl;
1008 } else if (targetTy == IR::ColorType) {
1010 case IR::StringType: opcode = V4Instr::ConvertStringToColor; break;
1013 } else if (targetTy == IR::ObjectType) {
1015 case IR::NullType: opcode = V4Instr::ConvertNullToObject; break;
1019 if (opcode != V4Instr::Noop) {
1021 conv.unaryop.output = dest;
1022 conv.unaryop.src = src;
1025 if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) {
1027 resolveUrl.unaryop.output = dest;
1028 resolveUrl.unaryop.src = dest;
1029 gen(V4Instr::ResolveUrl, resolveUrl);
1035 traceExpression(s->source, dest);
1039 void QV4CompilerPrivate::visitJump(IR::Jump *s)
1041 patches.append(Patch(s->target, bytecode.size()));
1044 i.offset = 0; // ### backpatch
1048 void QV4CompilerPrivate::visitCJump(IR::CJump *s)
1050 traceExpression(s->cond, currentReg);
1052 patches.append(Patch(s->iftrue, bytecode.size()));
1054 Instr::BranchTrue i;
1056 i.offset = 0; // ### backpatch
1060 void QV4CompilerPrivate::visitRet(IR::Ret *s)
1062 Q_ASSERT(s->expr != 0);
1064 int storeReg = currentReg;
1066 if (IR::Temp *temp = s->expr->asTemp()) {
1067 storeReg = temp->index;
1069 traceExpression(s->expr, storeReg);
1072 if (qmlBindingsTest) {
1073 Instr::TestV4Store test;
1074 test.reg = storeReg;
1076 case IR::StringType:
1077 test.regType = QMetaType::QString;
1080 test.regType = QMetaType::QUrl;
1083 test.regType = QMetaType::QColor;
1085 case IR::SGAnchorLineType:
1086 test.regType = QQmlMetaType::QQuickAnchorLineMetaTypeId();
1088 case IR::ObjectType:
1089 test.regType = QMetaType::QObjectStar;
1092 test.regType = QMetaType::Bool;
1095 test.regType = QMetaType::Int;
1098 test.regType = QMetaType::QReal;
1109 store.index = expression->property->index;
1110 store.reg = storeReg;
1111 store.exceptionId = exceptionId(s->line, s->column);
1115 void QV4Compiler::dump(const QByteArray &programData)
1117 const QV4Program *program = (const QV4Program *)programData.constData();
1119 qWarning() << "Program.bindings:" << program->bindings;
1120 qWarning() << "Program.dataLength:" << program->dataLength;
1121 qWarning() << "Program.subscriptions:" << program->subscriptions;
1122 qWarning() << "Program.indentifiers:" << program->identifiers;
1124 const int programSize = program->instructionCount;
1125 const char *start = program->instructions();
1126 const char *end = start + programSize;
1128 bc.dump(start, end);
1132 Clear the state associated with attempting to compile a specific binding.
1133 This does not clear the global "committed binding" states.
1135 void QV4CompilerPrivate::resetInstanceState()
1137 data = committed.data;
1138 exceptions = committed.exceptions;
1139 usedSubscriptionIds.clear();
1140 subscriptionIds = committed.subscriptionIds;
1141 registeredStrings = committed.registeredStrings;
1149 Mark the last compile as successful, and add it to the "committed data"
1152 Returns the index for the committed binding.
1154 int QV4CompilerPrivate::commitCompile()
1156 int rv = committed.count();
1157 committed.offsets << committed.bytecode.count();
1158 committed.dependencies << usedSubscriptionIds;
1159 committed.bytecode.append(bytecode.constData(), bytecode.size());
1160 committed.data = data;
1161 committed.exceptions = exceptions;
1162 committed.subscriptionIds = subscriptionIds;
1163 committed.registeredStrings = registeredStrings;
1167 bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node)
1169 resetInstanceState();
1171 if (expression->property->type == -1)
1174 AST::SourceLocation location;
1175 if (AST::ExpressionNode *astExpression = node->expressionCast()) {
1176 location = astExpression->firstSourceLocation();
1177 } else if (AST::Statement *astStatement = node->statementCast()) {
1178 if (AST::Block *block = AST::cast<AST::Block *>(astStatement))
1179 location = block->lbraceToken;
1180 else if (AST::IfStatement *ifStmt = AST::cast<AST::IfStatement *>(astStatement))
1181 location = ifStmt->ifToken;
1188 IR::Function thisFunction(&pool), *function = &thisFunction;
1190 QV4IRBuilder irBuilder(expression, engine);
1191 if (!irBuilder(function, node))
1194 bool discarded = false;
1195 qSwap(_discarded, discarded);
1196 qSwap(_function, function);
1197 trace(location.startLine, location.startColumn);
1198 qSwap(_function, function);
1199 qSwap(_discarded, discarded);
1201 if (qmlVerboseCompiler()) {
1202 QTextStream qerr(stderr, QIODevice::WriteOnly);
1204 qerr << "======== TODO ====== " << endl;
1206 qerr << "==================== " << endl;
1207 qerr << "\tline: " << location.startLine
1208 << "\tcolumn: " << location.startColumn
1210 foreach (IR::BasicBlock *bb, function->basicBlocks)
1215 if (discarded || subscriptionIds.count() > 0xFFFF || registeredStrings.count() > 0xFFFF || registerCount > 31)
1222 int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str)
1224 // ### string cleanup
1226 QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
1227 int offset = data.count();
1230 Instr::LoadString string;
1232 string.offset = offset;
1233 string.length = str.length();
1239 // Returns an identifier offset
1240 int QV4CompilerPrivate::registerString(const QString &string)
1242 Q_ASSERT(!string.isEmpty());
1244 QPair<int, int> *iter = registeredStrings.value(string);
1247 quint32 len = string.length();
1248 QByteArray lendata((const char *)&len, sizeof(quint32));
1249 QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar));
1250 strdata.prepend(lendata);
1251 int rv = data.count();
1254 iter = ®isteredStrings[string];
1255 *iter = qMakePair(registeredStrings.count(), rv);
1258 Instr::InitString reg;
1259 reg.offset = iter->first;
1260 reg.dataIdx = iter->second;
1266 Returns true if the current expression has not already subscribed to \a sub in currentBlockMask.
1268 bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub)
1270 QString str = sub.join(QLatin1String("."));
1272 int *iter = subscriptionIds.value(str);
1276 quint32 *uiter = usedSubscriptionIds.value(*iter);
1280 return !(*uiter & currentBlockMask);
1283 int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub)
1285 QString str = sub.join(QLatin1String("."));
1286 int *iter = subscriptionIds.value(str);
1288 int count = subscriptionIds.count();
1289 iter = &subscriptionIds[str];
1292 quint32 &u = usedSubscriptionIds[*iter];
1293 if (!(u & currentBlockMask)) {
1294 u |= currentBlockMask;
1295 usedSubscriptionIdsChanged = true;
1300 quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub)
1302 QString str = sub.join(QLatin1String("."));
1304 int *iter = subscriptionIds.value(str);
1305 Q_ASSERT(iter != 0);
1307 quint32 *uiter = usedSubscriptionIds.value(*iter);
1308 Q_ASSERT(uiter != 0);
1313 quint8 QV4CompilerPrivate::exceptionId(quint32 line, quint32 column)
1316 if (exceptions.count() < 0xFF) {
1317 rv = (quint8)exceptions.count();
1321 exceptions.append(e);
1326 quint8 QV4CompilerPrivate::exceptionId(QQmlJS::AST::ExpressionNode *n)
1329 if (n && exceptions.count() < 0xFF) {
1330 QQmlJS::AST::SourceLocation l = n->firstSourceLocation();
1331 rv = exceptionId(l.startLine, l.startColumn);
1336 QV4Compiler::QV4Compiler()
1337 : d(new QV4CompilerPrivate)
1339 qmlBindingsTest |= qmlBindingsTestEnv();
1342 QV4Compiler::~QV4Compiler()
1348 Returns true if any bindings were compiled.
1350 bool QV4Compiler::isValid() const
1352 return !d->committed.bytecode.isEmpty();
1356 -1 on failure, otherwise the binding index to use.
1358 int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine)
1360 if (!expression.expression.asAST()) return false;
1362 if (qmlDisableOptimizer() || !qmlEnableV4)
1365 d->expression = &expression;
1368 if (d->compile(expression.expression.asAST())) {
1369 return d->commitCompile();
1375 QByteArray QV4CompilerPrivate::buildSignalTable() const
1377 QHash<int, QList<QPair<int, quint32> > > table;
1379 for (int ii = 0; ii < committed.count(); ++ii) {
1380 const QQmlAssociationList<int, quint32> &deps = committed.dependencies.at(ii);
1381 for (QQmlAssociationList<int, quint32>::const_iterator iter = deps.begin(); iter != deps.end(); ++iter)
1382 table[iter->first].append(qMakePair(ii, iter->second));
1385 QVector<quint32> header;
1386 QVector<quint32> data;
1387 for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) {
1388 header.append(committed.subscriptionIds.count() + data.count());
1389 const QList<QPair<int, quint32> > &bindings = table[ii];
1390 data.append(bindings.count());
1391 for (int jj = 0; jj < bindings.count(); ++jj) {
1392 data.append(bindings.at(jj).first);
1393 data.append(bindings.at(jj).second);
1398 return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32));
1401 QByteArray QV4CompilerPrivate::buildExceptionData() const
1404 rv.resize(committed.exceptions.count() * sizeof(quint64));
1405 ::memcpy(rv.data(), committed.exceptions.constData(), rv.size());
1410 Returns the compiled program.
1412 QByteArray QV4Compiler::program() const
1414 QByteArray programData;
1418 prog.bindings = d->committed.count();
1421 QV4CompilerPrivate::Instr::Jump jump;
1424 for (int ii = 0; ii < d->committed.count(); ++ii) {
1425 jump.count = d->committed.count() - ii - 1;
1426 jump.count*= V4InstrMeta<V4Instr::Jump>::Size;
1427 jump.count+= d->committed.offsets.at(ii);
1432 QByteArray bytecode;
1433 bytecode.reserve(bc.size() + d->committed.bytecode.size());
1434 bytecode.append(bc.constData(), bc.size());
1435 bytecode.append(d->committed.bytecode.constData(), d->committed.bytecode.size());
1437 QByteArray data = d->committed.data;
1438 while (data.count() % 4) data.append('\0');
1439 prog.signalTableOffset = data.count();
1440 data += d->buildSignalTable();
1441 while (data.count() % 4) data.append('\0');
1442 prog.exceptionDataOffset = data.count();
1443 data += d->buildExceptionData();
1445 prog.dataLength = 4 * ((data.size() + 3) / 4);
1446 prog.subscriptions = d->committed.subscriptionIds.count();
1447 prog.identifiers = d->committed.registeredStrings.count();
1448 prog.instructionCount = bytecode.count();
1449 int size = sizeof(QV4Program) + bytecode.count();
1450 size += prog.dataLength;
1452 programData.resize(size);
1453 memcpy(programData.data(), &prog, sizeof(QV4Program));
1454 if (prog.dataLength)
1455 memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(),
1457 memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(),
1461 if (bindingsDump()) {
1462 qWarning().nospace() << "Subscription slots:";
1464 for (QQmlAssociationList<QString, int>::ConstIterator iter = d->committed.subscriptionIds.begin();
1465 iter != d->committed.subscriptionIds.end();
1467 qWarning().nospace() << " " << iter->first << "\t-> " << iter->second;
1470 QV4Compiler::dump(programData);
1476 void QV4Compiler::enableBindingsTest(bool e)
1479 qmlBindingsTest = true;
1481 qmlBindingsTest = qmlBindingsTestEnv();
1484 void QV4Compiler::enableV4(bool e)