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