Merge master <-> api_changes
[profile/ivi/qtdeclarative.git] / src / qml / qml / v4 / qv4compiler.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv4compiler_p.h"
43 #include "qv4compiler_p_p.h"
44 #include "qv4program_p.h"
45 #include "qv4ir_p.h"
46 #include "qv4irbuilder_p.h"
47
48 #include <private/qqmlglobal_p.h>
49 #include <private/qqmljsast_p.h>
50 #include <private/qqmlaccessors_p.h>
51 #include <private/qqmljsengine_p.h>
52
53 QT_BEGIN_NAMESPACE
54
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)
60
61 static bool qmlBindingsTest = false;
62 static bool qmlEnableV4 = true;
63
64 using namespace QQmlJS;
65 QV4CompilerPrivate::QV4CompilerPrivate()
66     : _function(0) , _block(0) , _discarded(false), registerCount(0)
67 {
68 }
69
70 //
71 // tracing
72 //
73 void QV4CompilerPrivate::trace(int line, int column)
74 {
75     bytecode.clear();
76
77     this->currentReg = _function->tempCount;
78     this->registerCount = qMax(this->registerCount, this->currentReg);
79
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));
83     }
84
85     QVector<IR::BasicBlock *> blocks;
86     trace(&blocks);
87     currentBlockMask = 0x00000001;
88
89
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);
99                 }
100             } else if (IR::Jump *j = terminator->asJump()) {
101                 if (j->target == next) {
102                     block->statements.resize(block->statements.size() - 1);
103                 }
104             }
105         }
106
107         block->offset = bytecode.size();
108
109         if (bytecode.isEmpty()) {
110             if (qmlBindingsTest || bindingsDump()) {
111                 Instr::BindingId id;
112                 id.column = column;
113                 id.line = line;
114                 gen(id);
115             }
116
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();
121                 data += strdata;
122
123                 Instr::EnableV4Test test;
124                 test.reg = 0;
125                 test.offset = offset;
126                 test.length = str.length();
127                 gen(test);
128             }
129         }
130
131         bool usic = false;
132         int patchesCount = patches.count();
133         qSwap(usedSubscriptionIdsChanged, usic);
134
135         int blockopIndex = bytecode.size();
136         Instr::Block blockop;
137         blockop.block = currentBlockMask;
138         gen(blockop);
139
140         foreach (IR::Stmt *s, block->statements) {
141             if (! _discarded)
142                 s->accept(this);
143         }
144
145         qSwap(usedSubscriptionIdsChanged, usic);
146
147         if (usic) {
148             if (currentBlockMask == 0x80000000) {
149                 discard();
150                 return;
151             }
152             currentBlockMask <<= 1;
153         } else if (! _discarded) {
154             const int adjust = bytecode.remove(blockopIndex);
155             // Correct patches
156             for (int ii = patchesCount; ii < patches.count(); ++ii) 
157                 patches[ii].offset -= adjust;
158         }
159     }
160
161 #ifdef DEBUG_IR_STRUCTURE
162     IR::IRDump dump;
163     for (int i = 0; i < blocks.size(); ++i) {
164         dump.basicblock(blocks.at(i));
165     }
166 #endif
167
168
169     if (! _discarded) {
170         // back patching
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;
175         }
176
177         patches.clear();
178     }
179 }
180
181 void QV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks)
182 {
183     for (int i = 0; i < _function->basicBlocks.size(); ++i) {
184         IR::BasicBlock *block = _function->basicBlocks.at(i);
185
186         while (! blocks->contains(block)) {
187             blocks->append(block);
188
189             if (IR::Stmt *terminator = block->terminator()) {
190                 if (IR::CJump *cj = terminator->asCJump())
191                     block = cj->iffalse;
192                 else if (IR::Jump *j = terminator->asJump())
193                     block = j->target;
194             }
195         }
196     }
197 }
198
199 void QV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r)
200 {
201     if (!e) {
202         discard();
203     } else {
204         qSwap(currentReg, r);
205         e->accept(this);
206         qSwap(currentReg, r);
207     }
208 }
209
210 //
211 // expressions
212 //
213 void QV4CompilerPrivate::visitConst(IR::Const *e)
214 {
215     switch (e->type) {
216     case IR::BoolType: {
217         Instr::LoadBool i;
218         i.reg = currentReg;
219         i.value = e->value;
220         gen(i);
221         } break;
222
223     case IR::IntType: {
224         Instr::LoadInt i;
225         i.reg = currentReg;
226         i.value = e->value;
227         gen(i);
228         } break;
229
230     case IR::RealType: {
231         Instr::LoadReal i;
232         i.reg = currentReg;
233         i.value = e->value;
234         gen(i);
235         } break;
236
237     default:
238         if (qmlVerboseCompiler())
239             qWarning() << Q_FUNC_INFO << "unexpected type";
240         discard();
241     }
242 }
243
244 void QV4CompilerPrivate::visitString(IR::String *e)
245 {
246     registerLiteralString(currentReg, e->value);
247 }
248
249 void QV4CompilerPrivate::visitName(IR::Name *e)
250 {
251     if (e->base) {
252         // fetch the object and store it in reg.
253         traceExpression(e->base, currentReg);
254     } else {
255         _subscribeName.clear();
256     }
257
258     if (e->storage == IR::Name::RootStorage) {
259
260         Instr::LoadRoot instr;
261         instr.reg = currentReg;
262         gen(instr);
263
264         if (e->symbol == IR::Name::IdObject) {
265             // The ID is a reference to the root object
266             return;
267         }
268
269     } else if (e->storage == IR::Name::ScopeStorage) {
270
271         Instr::LoadScope instr;
272         instr.reg = currentReg;
273         gen(instr);
274
275         _subscribeName << contextName();
276
277     } else if (e->storage == IR::Name::IdStorage) {
278
279         Instr::LoadId instr;
280         instr.reg = currentReg;
281         instr.index = e->idObject->idIndex;
282         gen(instr);
283
284         _subscribeName << QLatin1String("$$$ID_") + *e->id;
285
286         if (blockNeedsSubscription(_subscribeName)) {
287             Instr::SubscribeId sub;
288             sub.reg = currentReg;
289             sub.offset = subscriptionIndex(_subscribeName);
290             sub.index = instr.index;
291             gen(sub);
292         }
293
294         return;
295     } else {
296         // No action needed
297     }
298
299     switch (e->symbol) {
300     case IR::Name::Unbound: 
301     case IR::Name::IdObject: 
302     case IR::Name::Slot:
303     case IR::Name::Object: {
304         Q_ASSERT(!"Unreachable");
305         discard();
306     } break;
307
308     case IR::Name::AttachType: {
309         _subscribeName << *e->id;
310
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)
316             discard();
317         attached.id = e->declarativeType->attachedPropertiesId();
318         gen(attached);
319     } break;
320
321     case IR::Name::Property: {
322         _subscribeName << *e->id;
323
324         if (e->property->coreIndex == -1) {
325             QMetaProperty prop;
326             e->property->load(prop, QQmlEnginePrivate::get(engine));
327         }
328
329         const int propTy = e->property->propType;
330         QQmlRegisterType regType;
331
332         switch (propTy) {
333         case QMetaType::QReal:
334             regType = QRealType;
335             break;
336         case QMetaType::Bool:
337             regType = BoolType;
338             break;
339         case QMetaType::Int:
340             regType = IntType;
341             break;
342         case QMetaType::QString:
343             regType = QStringType;
344             break;
345         case QMetaType::QUrl:
346             regType = QUrlType;
347             break;
348         case QMetaType::QColor:
349             regType = QColorType;
350             break;
351
352         default:
353             if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
354                 regType = PODValueType;
355             } else if (QQmlMetaType::isQObject(propTy)) {
356                 regType = QObjectStarType;
357             } else {
358                 if (qmlVerboseCompiler())
359                     qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy);
360                 discard(); // Unsupported type
361                 return;
362             }
363
364             break;
365         } // switch
366
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;
374             gen(fetch);
375         } else {
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;
381                 gen(sub);
382             }
383
384             Instr::Fetch fetch;
385             fetch.reg = currentReg;
386             fetch.index = e->property->coreIndex;
387             fetch.exceptionId = exceptionId(e->line, e->column);
388             fetch.valueType = regType;
389             gen(fetch);
390         }
391
392     } break;
393     } // switch
394 }
395
396 void QV4CompilerPrivate::visitTemp(IR::Temp *e)
397 {
398     if (currentReg != e->index) {
399         Instr::Copy i;
400         i.reg = currentReg;
401         i.src = e->index;
402         gen(i);
403     }
404 }
405
406 void QV4CompilerPrivate::visitUnop(IR::Unop *e)
407 {
408     quint8 src = currentReg;
409     
410     if (IR::Temp *temp = e->expr->asTemp()) {
411         src = temp->index;
412     } else {
413         traceExpression(e->expr, src);
414     }
415
416     switch (e->op) {
417     case IR::OpInvalid:
418         Q_ASSERT(!"unreachable");
419         break;
420
421     case IR::OpIfTrue:
422         convertToBool(e->expr, src);
423         if (src != currentReg) {
424             Instr::Copy i;
425             i.reg = currentReg;
426             i.src = src;
427             gen(i);
428         }
429         break;
430
431     case IR::OpNot: {
432         Instr::UnaryNot i;
433         convertToBool(e->expr, src);
434         i.output = currentReg;
435         i.src = src;
436         gen(i);
437         } break;
438
439     case IR::OpUMinus:
440         if (e->expr->type == IR::RealType) {
441             Instr::UnaryMinusReal i;
442             i.output = currentReg;
443             i.src = src;
444             gen(i);
445         } else if (e->expr->type == IR::IntType) {
446             convertToReal(e->expr, currentReg);
447             Instr::UnaryMinusReal i;
448             i.output = currentReg;
449             i.src = src;
450             gen(i);
451         } else {
452             discard();
453         }
454         break;
455
456     case IR::OpUPlus:
457         if (e->expr->type == IR::RealType) {
458             Instr::UnaryPlusReal i;
459             i.output = currentReg;
460             i.src = src;
461             gen(i);
462         } else if (e->expr->type == IR::IntType) {
463             convertToReal(e->expr, currentReg);
464             Instr::UnaryPlusReal i;
465             i.output = currentReg;
466             i.src = src;
467             gen(i);
468         } else {
469             discard();
470         }
471         break;
472
473     case IR::OpCompl:
474         // TODO
475         discard();
476         break;
477
478     case IR::OpBitAnd:
479     case IR::OpBitOr:
480     case IR::OpBitXor:
481     case IR::OpAdd:
482     case IR::OpSub:
483     case IR::OpMul:
484     case IR::OpDiv:
485     case IR::OpMod:
486     case IR::OpLShift:
487     case IR::OpRShift:
488     case IR::OpURShift:
489     case IR::OpGt:
490     case IR::OpLt:
491     case IR::OpGe:
492     case IR::OpLe:
493     case IR::OpEqual:
494     case IR::OpNotEqual:
495     case IR::OpStrictEqual:
496     case IR::OpStrictNotEqual:
497     case IR::OpAnd:
498     case IR::OpOr:
499         Q_ASSERT(!"unreachable");
500         break;
501     } // switch
502 }
503
504 void QV4CompilerPrivate::convertToReal(IR::Expr *expr, int reg)
505 {
506     if (expr->type == IR::RealType)
507         return;
508
509     switch (expr->type) {
510     case IR::BoolType: {
511         Instr::ConvertBoolToReal i;
512         i.output = i.src = reg;
513         gen(i);
514         } break;
515
516     case IR::IntType: {
517         Instr::ConvertIntToReal i;
518         i.output = i.src = reg;
519         gen(i);
520         } break;
521
522     case IR::RealType:
523         // nothing to do
524         return;
525
526     default:
527         discard();
528         break;
529     } // switch
530 }
531
532 void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg)
533 {
534     if (expr->type == IR::IntType)
535         return;
536
537     switch (expr->type) {
538     case IR::BoolType: {
539         Instr::ConvertBoolToInt i;
540         i.output = i.src = reg;
541         gen(i);
542         } break;
543
544     case IR::IntType:
545         // nothing to do
546         return;
547
548     case IR::RealType: {
549         Instr::ConvertRealToInt i;
550         i.output = i.src = reg;
551         gen(i);
552         } break;
553
554     default:
555         discard();
556         break;
557     } // switch
558 }
559
560 void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg)
561 {
562     if (expr->type == IR::BoolType)
563         return;
564
565     switch (expr->type) {
566     case IR::BoolType:
567         // nothing to do
568         break;
569
570     case IR::IntType: {
571         Instr::ConvertIntToBool i;
572         i.output = i.src = reg;
573         gen(i);
574         } break;
575
576     case IR::RealType: {
577         Instr::ConvertRealToBool i;
578         i.output = i.src = reg;
579         gen(i);
580         } return;
581
582     case IR::StringType: {
583         Instr::ConvertStringToBool i;
584         i.output = i.src = reg;
585         gen(i);
586         } return;
587
588     case IR::ColorType: {
589         Instr::ConvertColorToBool i;
590         i.output = i.src = reg;
591         gen(i);
592         } return;
593
594     default:
595         discard();
596         break;
597     } // switch
598 }
599
600 quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e)
601 {
602     switch (e->op) {
603     case IR::OpInvalid:
604         return V4Instr::Noop;
605
606     case IR::OpIfTrue:
607     case IR::OpNot:
608     case IR::OpUMinus:
609     case IR::OpUPlus:
610     case IR::OpCompl:
611         return V4Instr::Noop;
612
613     case IR::OpBitAnd:
614         return V4Instr::BitAndInt;
615
616     case IR::OpBitOr:
617         return V4Instr::BitOrInt;
618
619     case IR::OpBitXor:
620         return V4Instr::BitXorInt;
621
622     case IR::OpAdd:
623         if (e->type == IR::StringType)
624             return V4Instr::AddString;
625         return V4Instr::AddReal;
626
627     case IR::OpSub:
628         return V4Instr::SubReal;
629
630     case IR::OpMul:
631         return V4Instr::MulReal;
632
633     case IR::OpDiv:
634         return V4Instr::DivReal;
635
636     case IR::OpMod:
637         return V4Instr::ModReal;
638
639     case IR::OpLShift:
640         return V4Instr::LShiftInt;
641
642     case IR::OpRShift:
643         return V4Instr::RShiftInt;
644
645     case IR::OpURShift:
646         return V4Instr::URShiftInt;
647
648     case IR::OpGt:
649         if (e->left->type == IR::StringType)
650             return V4Instr::GtString;
651         return V4Instr::GtReal;
652
653     case IR::OpLt:
654         if (e->left->type == IR::StringType)
655             return V4Instr::LtString;
656         return V4Instr::LtReal;
657
658     case IR::OpGe:
659         if (e->left->type == IR::StringType)
660             return V4Instr::GeString;
661         return V4Instr::GeReal;
662
663     case IR::OpLe:
664         if (e->left->type == IR::StringType)
665             return V4Instr::LeString;
666         return V4Instr::LeReal;
667
668     case IR::OpEqual:
669         if (e->left->type == IR::StringType)
670             return V4Instr::EqualString;
671         return V4Instr::EqualReal;
672
673     case IR::OpNotEqual:
674         if (e->left->type == IR::StringType)
675             return V4Instr::NotEqualString;
676         return V4Instr::NotEqualReal;
677
678     case IR::OpStrictEqual:
679         if (e->left->type == IR::StringType)
680             return V4Instr::StrictEqualString;
681         return V4Instr::StrictEqualReal;
682
683     case IR::OpStrictNotEqual:
684         if (e->left->type == IR::StringType)
685             return V4Instr::StrictNotEqualString;
686         return V4Instr::StrictNotEqualReal;
687
688     case IR::OpAnd:
689     case IR::OpOr:
690         return V4Instr::Noop;
691
692     } // switch
693
694     return V4Instr::Noop;
695 }
696
697 void QV4CompilerPrivate::visitBinop(IR::Binop *e)
698 {
699     int left = currentReg;
700     int right = currentReg + 1; 
701
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;
704     else
705         traceExpression(e->left, left);
706
707     if (IR::Temp *t = e->right->asTemp())
708         right = t->index;
709     else
710         traceExpression(e->right, right);
711
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)
716                                  << "' and `"
717                                  << IR::binaryOperator(e->right->type)
718                                  << "'";
719         discard();
720         return;
721     }
722
723     switch (e->op) {
724     case IR::OpInvalid:
725         discard();
726         break;
727
728     // unary
729     case IR::OpIfTrue:
730     case IR::OpNot:
731     case IR::OpUMinus:
732     case IR::OpUPlus:
733     case IR::OpCompl:
734         discard();
735         break;
736
737     case IR::OpBitAnd:
738     case IR::OpBitOr:
739     case IR::OpBitXor:
740     case IR::OpLShift:
741     case IR::OpRShift:
742     case IR::OpURShift:
743         convertToInt(e->left, left);
744         convertToInt(e->right, right);
745         break;
746
747     case IR::OpAdd:
748         if (e->type != IR::StringType) {
749             convertToReal(e->left, left);
750             convertToReal(e->right, right);
751         }
752         break;
753
754     case IR::OpSub:
755     case IR::OpMul:
756     case IR::OpDiv:
757     case IR::OpMod:
758         convertToReal(e->left, left);
759         convertToReal(e->right, right);
760         break;
761
762     case IR::OpGt:
763     case IR::OpLt:
764     case IR::OpGe:
765     case IR::OpLe:
766     case IR::OpEqual:
767     case IR::OpNotEqual:
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);
773         }
774         break;
775
776     case IR::OpAnd:
777     case IR::OpOr:
778         discard(); // ### unreachable
779         break;
780     } // switch
781
782     const quint8 opcode = instructionOpcode(e);
783     if (opcode != V4Instr::Noop) {
784         V4Instr instr;
785         instr.binaryop.output = currentReg;
786         instr.binaryop.left = left;
787         instr.binaryop.right = right;
788         gen(static_cast<V4Instr::Type>(opcode), instr);
789     }
790 }
791
792 void QV4CompilerPrivate::visitCall(IR::Call *call)
793 {
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);
798
799             switch (name->builtin) {
800             case IR::NoBuiltinSymbol:
801                 break;
802
803             case IR::MathSinBultinFunction: {
804                 Instr::MathSinReal i;
805                 i.output = i.src = currentReg;
806                 gen(i);
807                 } return;
808
809             case IR::MathCosBultinFunction: {
810                 Instr::MathCosReal i;
811                 i.output = i.src = currentReg;
812                 gen(i);
813                 } return;
814
815             case IR::MathRoundBultinFunction: {
816                 Instr::MathRoundReal i;
817                 i.output = i.src = currentReg;
818                 gen(i);
819                 } return;
820
821             case IR::MathFloorBultinFunction: {
822                 Instr::MathFloorReal i;
823                 i.output = i.src = currentReg;
824                 gen(i);
825                 } return;
826
827             case IR::MathPIBuiltinConstant:
828                 break;
829             } // switch
830         }
831     }
832
833     if (qmlVerboseCompiler())
834         qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__;
835     discard();
836 }
837
838
839 //
840 // statements
841 //
842 void QV4CompilerPrivate::visitExp(IR::Exp *s)
843 {
844     traceExpression(s->expr, currentReg);
845 }
846
847 void QV4CompilerPrivate::visitMove(IR::Move *s)
848 {
849     IR::Temp *target = s->target->asTemp();
850     Q_ASSERT(target != 0);
851
852     quint8 dest = target->index;
853
854     if (target->type != s->source->type) {
855         quint8 src = dest;
856
857         if (IR::Temp *t = s->source->asTemp()) 
858             src = t->index;
859         else
860             traceExpression(s->source, dest);
861
862         V4Instr::Type opcode = V4Instr::Noop;
863         IR::Type targetTy = s->target->type;
864         IR::Type sourceTy = s->source->type;
865
866         if (sourceTy == IR::UrlType) {
867             switch (targetTy) {
868             case IR::BoolType:
869             case IR::StringType:
870                 // nothing to do. V4 will generate optimized
871                 // url-to-xxx conversions.
872                 break;
873             default: {
874                 // generate a UrlToString conversion and fix
875                 // the type of the source expression.
876                 V4Instr conv;
877                 conv.unaryop.output = V4Instr::ConvertUrlToString;
878                 conv.unaryop.src = src;
879                 gen(opcode, conv);
880
881                 sourceTy = IR::StringType;
882                 break;
883             }
884             } // switch
885         }
886
887         if (targetTy == IR::BoolType) {
888             switch (sourceTy) {
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;
894             default: break;
895             } // switch
896         } else if (targetTy == IR::IntType) {
897             switch (sourceTy) {
898             case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break;
899             case IR::RealType: {
900                 if (s->isMoveForReturn)
901                     opcode = V4Instr::MathRoundReal;
902                 else
903                     opcode = V4Instr::ConvertRealToInt;
904                 break;
905             }
906             case IR::StringType: opcode = V4Instr::ConvertStringToInt; break;
907             default: break;
908             } // switch
909         } else if (targetTy == IR::RealType) {
910             switch (sourceTy) {
911             case IR::BoolType: opcode = V4Instr::ConvertBoolToReal; break;
912             case IR::IntType: opcode = V4Instr::ConvertIntToReal; break;
913             case IR::StringType: opcode = V4Instr::ConvertStringToReal; break;
914             default: break;
915             } // switch
916         } else if (targetTy == IR::StringType) {
917             switch (sourceTy) {
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;
923             default: break;
924             } // switch
925         } else if (targetTy == IR::UrlType) {
926             V4Instr convToString;
927             convToString.unaryop.output = dest;
928             convToString.unaryop.src = src;
929
930             // try to convert the source expression to a string.
931             switch (sourceTy) {
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;
936             default: break;
937             } // switch
938
939             if (sourceTy == IR::StringType)
940                 opcode = V4Instr::ConvertStringToUrl;
941         } else if (targetTy == IR::ColorType) {
942             switch (sourceTy) {
943             case IR::StringType: opcode = V4Instr::ConvertStringToColor; break;
944             default: break;
945             } // switch
946         }
947         if (opcode != V4Instr::Noop) {
948             V4Instr conv;
949             conv.unaryop.output = dest;
950             conv.unaryop.src = src;
951             gen(opcode, conv);
952
953             if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) {
954                 V4Instr resolveUrl;
955                 resolveUrl.unaryop.output = dest;
956                 resolveUrl.unaryop.src = dest;
957                 gen(V4Instr::ResolveUrl, resolveUrl);
958             }
959         } else {
960             discard();
961         }
962     } else {
963         traceExpression(s->source, dest);
964     }
965 }
966
967 void QV4CompilerPrivate::visitJump(IR::Jump *s)
968 {
969     patches.append(Patch(s->target, bytecode.size()));
970
971     Instr::Branch i;
972     i.offset = 0; // ### backpatch
973     gen(i);
974 }
975
976 void QV4CompilerPrivate::visitCJump(IR::CJump *s)
977 {
978     traceExpression(s->cond, currentReg);
979
980     patches.append(Patch(s->iftrue, bytecode.size()));
981
982     Instr::BranchTrue i;
983     i.reg = currentReg;
984     i.offset = 0; // ### backpatch
985     gen(i);
986 }
987
988 void QV4CompilerPrivate::visitRet(IR::Ret *s)
989 {
990     Q_ASSERT(s->expr != 0);
991
992     int storeReg = currentReg;
993
994     if (IR::Temp *temp = s->expr->asTemp()) {
995         storeReg = temp->index;
996     } else {
997         traceExpression(s->expr, storeReg);
998     }
999
1000     if (qmlBindingsTest) {
1001         Instr::TestV4Store test;
1002         test.reg = storeReg;
1003         switch (s->type) {
1004         case IR::StringType:
1005             test.regType = QMetaType::QString;
1006             break;
1007         case IR::UrlType:
1008             test.regType = QMetaType::QUrl;
1009             break;
1010         case IR::ColorType:
1011             test.regType = QMetaType::QColor;
1012             break;
1013         case IR::SGAnchorLineType:
1014             test.regType = QQmlMetaType::QQuickAnchorLineMetaTypeId();
1015             break;
1016         case IR::ObjectType:
1017             test.regType = QMetaType::QObjectStar;
1018             break;
1019         case IR::BoolType:
1020             test.regType = QMetaType::Bool;
1021             break;
1022         case IR::IntType:
1023             test.regType = QMetaType::Int;
1024             break;
1025         case IR::RealType:
1026             test.regType = QMetaType::QReal;
1027             break;
1028         default:
1029             discard();
1030             return;
1031         }
1032         gen(test);
1033     }
1034
1035     Instr::Store store;
1036     store.output = 0;
1037     store.index = expression->property->index;
1038     store.reg = storeReg;
1039     store.exceptionId = exceptionId(s->line, s->column);
1040     gen(store);
1041 }
1042
1043 void QV4Compiler::dump(const QByteArray &programData)
1044 {
1045     const QV4Program *program = (const QV4Program *)programData.constData();
1046
1047     qWarning() << "Program.bindings:" << program->bindings;
1048     qWarning() << "Program.dataLength:" << program->dataLength;
1049     qWarning() << "Program.subscriptions:" << program->subscriptions;
1050     qWarning() << "Program.indentifiers:" << program->identifiers;
1051
1052     const int programSize = program->instructionCount;
1053     const char *start = program->instructions();
1054     const char *end = start + programSize;
1055     Bytecode bc;
1056     bc.dump(start, end);
1057 }
1058
1059 /*!
1060 Clear the state associated with attempting to compile a specific binding.
1061 This does not clear the global "committed binding" states.
1062 */
1063 void QV4CompilerPrivate::resetInstanceState()
1064 {
1065     data = committed.data;
1066     exceptions = committed.exceptions;
1067     usedSubscriptionIds.clear();
1068     subscriptionIds = committed.subscriptionIds;
1069     registeredStrings = committed.registeredStrings;
1070     bytecode.clear();
1071     patches.clear();
1072     pool.clear();
1073     currentReg = 0;
1074 }
1075
1076 /*!
1077 Mark the last compile as successful, and add it to the "committed data"
1078 section.
1079
1080 Returns the index for the committed binding.
1081 */
1082 int QV4CompilerPrivate::commitCompile()
1083 {
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;
1092     return rv;
1093 }
1094
1095 bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node)
1096 {
1097     resetInstanceState();
1098
1099     if (expression->property->type == -1)
1100         return false;
1101
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;
1110         else
1111             return false;
1112     } else {
1113         return false;
1114     }
1115
1116     IR::Function thisFunction(&pool), *function = &thisFunction;
1117
1118     QV4IRBuilder irBuilder(expression, engine);
1119     if (!irBuilder(function, node))
1120         return false;
1121
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);
1128
1129     if (qmlVerboseCompiler()) {
1130         QTextStream qerr(stderr, QIODevice::WriteOnly);
1131         if (discarded)
1132             qerr << "======== TODO ====== " << endl;
1133         else 
1134             qerr << "==================== " << endl;
1135         qerr << "\tline: " << location.startLine
1136              << "\tcolumn: " << location.startColumn
1137              << endl;
1138         foreach (IR::BasicBlock *bb, function->basicBlocks)
1139             bb->dump(qerr);
1140         qerr << endl;
1141     }
1142
1143     if (discarded || subscriptionIds.count() > 0xFFFF || registeredStrings.count() > 0xFFFF || registerCount > 31)
1144         return false;
1145
1146     return true;
1147 }
1148
1149 // Returns a reg
1150 int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str)
1151 {
1152     // ### string cleanup
1153
1154     QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
1155     int offset = data.count();
1156     data += strdata;
1157
1158     Instr::LoadString string;
1159     string.reg = reg;
1160     string.offset = offset;
1161     string.length = str.length();
1162     gen(string);
1163
1164     return reg;
1165 }
1166
1167 // Returns an identifier offset
1168 int QV4CompilerPrivate::registerString(const QString &string)
1169 {
1170     Q_ASSERT(!string.isEmpty());
1171
1172     QPair<int, int> *iter = registeredStrings.value(string);
1173
1174     if (!iter) {
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();
1180         data += strdata;
1181
1182         iter = &registeredStrings[string];
1183         *iter = qMakePair(registeredStrings.count(), rv);
1184     } 
1185
1186     Instr::InitString reg;
1187     reg.offset = iter->first;
1188     reg.dataIdx = iter->second;
1189     gen(reg);
1190     return reg.offset;
1191 }
1192
1193 /*!
1194 Returns true if the current expression has not already subscribed to \a sub in currentBlockMask.
1195 */
1196 bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub)
1197 {
1198     QString str = sub.join(QLatin1String("."));
1199
1200     int *iter = subscriptionIds.value(str);
1201     if (!iter)
1202         return true;
1203
1204     quint32 *uiter = usedSubscriptionIds.value(*iter);
1205     if (!uiter)
1206         return true;
1207     else
1208         return !(*uiter & currentBlockMask);
1209 }
1210
1211 int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub)
1212 {
1213     QString str = sub.join(QLatin1String("."));
1214     int *iter = subscriptionIds.value(str);
1215     if (!iter) {
1216         int count = subscriptionIds.count();
1217         iter = &subscriptionIds[str];
1218         *iter = count;
1219     }
1220     quint32 &u = usedSubscriptionIds[*iter];
1221     if (!(u & currentBlockMask)) {
1222         u |= currentBlockMask;
1223         usedSubscriptionIdsChanged = true;
1224     }
1225     return *iter;
1226 }
1227
1228 quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub)
1229 {
1230     QString str = sub.join(QLatin1String("."));
1231
1232     int *iter = subscriptionIds.value(str);
1233     Q_ASSERT(iter != 0);
1234
1235     quint32 *uiter = usedSubscriptionIds.value(*iter);
1236     Q_ASSERT(uiter != 0);
1237
1238     return *uiter;
1239 }
1240
1241 quint8 QV4CompilerPrivate::exceptionId(quint32 line, quint32 column)
1242 {
1243     quint8 rv = 0xFF;
1244     if (exceptions.count() < 0xFF) {
1245         rv = (quint8)exceptions.count();
1246         quint64 e = line;
1247         e <<= 32;
1248         e |= column;
1249         exceptions.append(e);
1250     }
1251     return rv;
1252 }
1253
1254 quint8 QV4CompilerPrivate::exceptionId(QQmlJS::AST::ExpressionNode *n)
1255 {
1256     quint8 rv = 0xFF;
1257     if (n && exceptions.count() < 0xFF) {
1258         QQmlJS::AST::SourceLocation l = n->firstSourceLocation();
1259         rv = exceptionId(l.startLine, l.startColumn);
1260     }
1261     return rv;
1262 }
1263
1264 QV4Compiler::QV4Compiler()
1265 : d(new QV4CompilerPrivate)
1266 {
1267     qmlBindingsTest |= qmlBindingsTestEnv();
1268 }
1269
1270 QV4Compiler::~QV4Compiler()
1271 {
1272     delete d; d = 0;
1273 }
1274
1275 /* 
1276 Returns true if any bindings were compiled.
1277 */
1278 bool QV4Compiler::isValid() const
1279 {
1280     return !d->committed.bytecode.isEmpty();
1281 }
1282
1283 /* 
1284 -1 on failure, otherwise the binding index to use.
1285 */
1286 int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine)
1287 {
1288     if (!expression.expression.asAST()) return false;
1289
1290     if (!qmlExperimental() && expression.property->isValueTypeSubProperty)
1291         return -1;
1292
1293     if (qmlDisableOptimizer() || !qmlEnableV4)
1294         return -1;
1295
1296     d->expression = &expression;
1297     d->engine = engine;
1298
1299     if (d->compile(expression.expression.asAST())) {
1300         return d->commitCompile();
1301     } else {
1302         return -1;
1303     }
1304 }
1305
1306 QByteArray QV4CompilerPrivate::buildSignalTable() const
1307 {
1308     QHash<int, QList<QPair<int, quint32> > > table;
1309
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));
1314     }
1315
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);
1325         }
1326     }
1327     header << data;
1328
1329     return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32));
1330 }
1331
1332 QByteArray QV4CompilerPrivate::buildExceptionData() const
1333 {
1334     QByteArray rv;
1335     rv.resize(committed.exceptions.count() * sizeof(quint64));
1336     ::memcpy(rv.data(), committed.exceptions.constData(), rv.size());
1337     return rv;
1338 }
1339
1340 /* 
1341 Returns the compiled program.
1342 */
1343 QByteArray QV4Compiler::program() const
1344 {
1345     QByteArray programData;
1346
1347     if (isValid()) {
1348         QV4Program prog;
1349         prog.bindings = d->committed.count();
1350
1351         Bytecode bc;
1352         QV4CompilerPrivate::Instr::Jump jump;
1353         jump.reg = -1;
1354
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);
1359             bc.append(jump);
1360         }
1361
1362
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());
1367
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();
1375
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;
1382
1383         programData.resize(size);
1384         memcpy(programData.data(), &prog, sizeof(QV4Program));
1385         if (prog.dataLength)
1386             memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(), 
1387                    data.size());
1388         memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(),
1389                bytecode.count());
1390     } 
1391
1392     if (bindingsDump()) {
1393         qWarning().nospace() << "Subscription slots:";
1394
1395         for (QQmlAssociationList<QString, int>::ConstIterator iter = d->committed.subscriptionIds.begin();
1396                 iter != d->committed.subscriptionIds.end();
1397                 ++iter) {
1398             qWarning().nospace() << "    " << iter->first << "\t-> " << iter->second;
1399         }
1400
1401         QV4Compiler::dump(programData);
1402     }
1403
1404     return programData;
1405 }
1406
1407 void QV4Compiler::enableBindingsTest(bool e)
1408 {
1409     if (e)
1410         qmlBindingsTest = true;
1411     else 
1412         qmlBindingsTest = qmlBindingsTestEnv();
1413 }
1414
1415 void QV4Compiler::enableV4(bool e)
1416 {
1417     qmlEnableV4 = e;
1418 }
1419
1420 QT_END_NAMESPACE