Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / declarative / 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 QtDeclarative 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/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         case QMetaType::QUrl:
344             regType = QUrlType;
345             break;
346
347         default:
348             if (propTy == qMetaTypeId<QDeclarative1AnchorLine>()) {
349                 regType = PODValueType;
350             } else if (propTy == QDeclarativeMetaType::QQuickAnchorLineMetaTypeId()) {
351                 regType = PODValueType;
352             } else if (QDeclarativeMetaType::isQObject(propTy)) {
353                 regType = QObjectStarType;
354             } else {
355                 if (qmlVerboseCompiler())
356                     qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy);
357                 discard(); // Unsupported type
358                 return;
359             }
360
361             break;
362         } // switch
363
364         if (e->property->hasAccessors()) {
365             Instr::FetchAndSubscribe fetch;
366             fetch.reg = currentReg;
367             fetch.subscription = subscriptionIndex(_subscribeName);
368             fetch.exceptionId = exceptionId(e->line, e->column);
369             fetch.valueType = regType;
370             fetch.property = *e->property;
371             gen(fetch);
372         } else {
373             if (blockNeedsSubscription(_subscribeName) && e->property->notifyIndex != -1) {
374                 Instr::Subscribe sub;
375                 sub.reg = currentReg;
376                 sub.offset = subscriptionIndex(_subscribeName);
377                 sub.index = e->property->notifyIndex;
378                 gen(sub);
379             }
380
381             Instr::Fetch fetch;
382             fetch.reg = currentReg;
383             fetch.index = e->property->coreIndex;
384             fetch.exceptionId = exceptionId(e->line, e->column);
385             fetch.valueType = regType;
386             gen(fetch);
387         }
388
389     } break;
390     } // switch
391 }
392
393 void QV4CompilerPrivate::visitTemp(IR::Temp *e)
394 {
395     if (currentReg != e->index) {
396         Instr::Copy i;
397         i.reg = currentReg;
398         i.src = e->index;
399         gen(i);
400     }
401 }
402
403 void QV4CompilerPrivate::visitUnop(IR::Unop *e)
404 {
405     quint8 src = currentReg;
406     
407     if (IR::Temp *temp = e->expr->asTemp()) {
408         src = temp->index;
409     } else {
410         traceExpression(e->expr, src);
411     }
412
413     switch (e->op) {
414     case IR::OpInvalid:
415         Q_ASSERT(!"unreachable");
416         break;
417
418     case IR::OpIfTrue:
419         convertToBool(e->expr, src);
420         if (src != currentReg) {
421             Instr::Copy i;
422             i.reg = currentReg;
423             i.src = src;
424             gen(i);
425         }
426         break;
427
428     case IR::OpNot: {
429         Instr::UnaryNot i;
430         convertToBool(e->expr, src);
431         i.output = currentReg;
432         i.src = src;
433         gen(i);
434         } break;
435
436     case IR::OpUMinus:
437         if (e->expr->type == IR::RealType) {
438             Instr::UnaryMinusReal i;
439             i.output = currentReg;
440             i.src = src;
441             gen(i);
442         } else if (e->expr->type == IR::IntType) {
443             convertToReal(e->expr, currentReg);
444             Instr::UnaryMinusReal i;
445             i.output = currentReg;
446             i.src = src;
447             gen(i);
448         } else {
449             discard();
450         }
451         break;
452
453     case IR::OpUPlus:
454         if (e->expr->type == IR::RealType) {
455             Instr::UnaryPlusReal i;
456             i.output = currentReg;
457             i.src = src;
458             gen(i);
459         } else if (e->expr->type == IR::IntType) {
460             convertToReal(e->expr, currentReg);
461             Instr::UnaryPlusReal i;
462             i.output = currentReg;
463             i.src = src;
464             gen(i);
465         } else {
466             discard();
467         }
468         break;
469
470     case IR::OpCompl:
471         // TODO
472         discard();
473         break;
474
475     case IR::OpBitAnd:
476     case IR::OpBitOr:
477     case IR::OpBitXor:
478     case IR::OpAdd:
479     case IR::OpSub:
480     case IR::OpMul:
481     case IR::OpDiv:
482     case IR::OpMod:
483     case IR::OpLShift:
484     case IR::OpRShift:
485     case IR::OpURShift:
486     case IR::OpGt:
487     case IR::OpLt:
488     case IR::OpGe:
489     case IR::OpLe:
490     case IR::OpEqual:
491     case IR::OpNotEqual:
492     case IR::OpStrictEqual:
493     case IR::OpStrictNotEqual:
494     case IR::OpAnd:
495     case IR::OpOr:
496         Q_ASSERT(!"unreachable");
497         break;
498     } // switch
499 }
500
501 void QV4CompilerPrivate::convertToReal(IR::Expr *expr, int reg)
502 {
503     if (expr->type == IR::RealType)
504         return;
505
506     switch (expr->type) {
507     case IR::BoolType: {
508         Instr::ConvertBoolToReal i;
509         i.output = i.src = reg;
510         gen(i);
511         } break;
512
513     case IR::IntType: {
514         Instr::ConvertIntToReal i;
515         i.output = i.src = reg;
516         gen(i);
517         } break;
518
519     case IR::RealType:
520         // nothing to do
521         return;
522
523     default:
524         discard();
525         break;
526     } // switch
527 }
528
529 void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg)
530 {
531     if (expr->type == IR::IntType)
532         return;
533
534     switch (expr->type) {
535     case IR::BoolType: {
536         Instr::ConvertBoolToInt i;
537         i.output = i.src = reg;
538         gen(i);
539         } break;
540
541     case IR::IntType:
542         // nothing to do
543         return;
544
545     case IR::RealType: {
546         Instr::ConvertRealToInt i;
547         i.output = i.src = reg;
548         gen(i);
549         } break;
550
551     default:
552         discard();
553         break;
554     } // switch
555 }
556
557 void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg)
558 {
559     if (expr->type == IR::BoolType)
560         return;
561
562     switch (expr->type) {
563     case IR::BoolType:
564         // nothing to do
565         break;
566
567     case IR::IntType: {
568         Instr::ConvertIntToBool i;
569         i.output = i.src = reg;
570         gen(i);
571         } break;
572
573     case IR::RealType: {
574         Instr::ConvertRealToBool i;
575         i.output = i.src = reg;
576         gen(i);
577         } return;
578
579     case IR::StringType: {
580         Instr::ConvertStringToBool i;
581         i.output = i.src = reg;
582         gen(i);
583         } return;
584
585     default:
586         discard();
587         break;
588     } // switch
589 }
590
591 quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e)
592 {
593     switch (e->op) {
594     case IR::OpInvalid:
595         return V4Instr::Noop;
596
597     case IR::OpIfTrue:
598     case IR::OpNot:
599     case IR::OpUMinus:
600     case IR::OpUPlus:
601     case IR::OpCompl:
602         return V4Instr::Noop;
603
604     case IR::OpBitAnd:
605         return V4Instr::BitAndInt;
606
607     case IR::OpBitOr:
608         return V4Instr::BitOrInt;
609
610     case IR::OpBitXor:
611         return V4Instr::BitXorInt;
612
613     case IR::OpAdd:
614         if (e->type == IR::StringType)
615             return V4Instr::AddString;
616         return V4Instr::AddReal;
617
618     case IR::OpSub:
619         return V4Instr::SubReal;
620
621     case IR::OpMul:
622         return V4Instr::MulReal;
623
624     case IR::OpDiv:
625         return V4Instr::DivReal;
626
627     case IR::OpMod:
628         return V4Instr::ModReal;
629
630     case IR::OpLShift:
631         return V4Instr::LShiftInt;
632
633     case IR::OpRShift:
634         return V4Instr::RShiftInt;
635
636     case IR::OpURShift:
637         return V4Instr::URShiftInt;
638
639     case IR::OpGt:
640         if (e->left->type == IR::StringType)
641             return V4Instr::GtString;
642         return V4Instr::GtReal;
643
644     case IR::OpLt:
645         if (e->left->type == IR::StringType)
646             return V4Instr::LtString;
647         return V4Instr::LtReal;
648
649     case IR::OpGe:
650         if (e->left->type == IR::StringType)
651             return V4Instr::GeString;
652         return V4Instr::GeReal;
653
654     case IR::OpLe:
655         if (e->left->type == IR::StringType)
656             return V4Instr::LeString;
657         return V4Instr::LeReal;
658
659     case IR::OpEqual:
660         if (e->left->type == IR::StringType)
661             return V4Instr::EqualString;
662         return V4Instr::EqualReal;
663
664     case IR::OpNotEqual:
665         if (e->left->type == IR::StringType)
666             return V4Instr::NotEqualString;
667         return V4Instr::NotEqualReal;
668
669     case IR::OpStrictEqual:
670         if (e->left->type == IR::StringType)
671             return V4Instr::StrictEqualString;
672         return V4Instr::StrictEqualReal;
673
674     case IR::OpStrictNotEqual:
675         if (e->left->type == IR::StringType)
676             return V4Instr::StrictNotEqualString;
677         return V4Instr::StrictNotEqualReal;
678
679     case IR::OpAnd:
680     case IR::OpOr:
681         return V4Instr::Noop;
682
683     } // switch
684
685     return V4Instr::Noop;
686 }
687
688 void QV4CompilerPrivate::visitBinop(IR::Binop *e)
689 {
690     int left = currentReg;
691     int right = currentReg + 1; 
692
693     if (e->left->asTemp() && e->type != IR::StringType)  // Not sure if the e->type != String test is needed
694         left = e->left->asTemp()->index;
695     else
696         traceExpression(e->left, left);
697
698     if (IR::Temp *t = e->right->asTemp())
699         right = t->index;
700     else
701         traceExpression(e->right, right);
702
703     if (e->left->type != e->right->type) {
704         if (qmlVerboseCompiler())
705             qWarning().nospace() << "invalid operands to binary operator " << IR::binaryOperator(e->op)
706                                  << "(`" << IR::binaryOperator(e->left->type)
707                                  << "' and `"
708                                  << IR::binaryOperator(e->right->type)
709                                  << "'";
710         discard();
711         return;
712     }
713
714     switch (e->op) {
715     case IR::OpInvalid:
716         discard();
717         break;
718
719     // unary
720     case IR::OpIfTrue:
721     case IR::OpNot:
722     case IR::OpUMinus:
723     case IR::OpUPlus:
724     case IR::OpCompl:
725         discard();
726         break;
727
728     case IR::OpBitAnd:
729     case IR::OpBitOr:
730     case IR::OpBitXor:
731     case IR::OpLShift:
732     case IR::OpRShift:
733     case IR::OpURShift:
734         convertToInt(e->left, left);
735         convertToInt(e->right, right);
736         break;
737
738     case IR::OpAdd:
739         if (e->type != IR::StringType) {
740             convertToReal(e->left, left);
741             convertToReal(e->right, right);
742         }
743         break;
744
745     case IR::OpSub:
746     case IR::OpMul:
747     case IR::OpDiv:
748     case IR::OpMod:
749         convertToReal(e->left, left);
750         convertToReal(e->right, right);
751         break;
752
753     case IR::OpGt:
754     case IR::OpLt:
755     case IR::OpGe:
756     case IR::OpLe:
757     case IR::OpEqual:
758     case IR::OpNotEqual:
759     case IR::OpStrictEqual:
760     case IR::OpStrictNotEqual:
761         if (e->left->type != IR::StringType) {
762             convertToReal(e->left, left);
763             convertToReal(e->right, right);
764         }
765         break;
766
767     case IR::OpAnd:
768     case IR::OpOr:
769         discard(); // ### unreachable
770         break;
771     } // switch
772
773     const quint8 opcode = instructionOpcode(e);
774     if (opcode != V4Instr::Noop) {
775         V4Instr instr;
776         instr.binaryop.output = currentReg;
777         instr.binaryop.left = left;
778         instr.binaryop.right = right;
779         gen(static_cast<V4Instr::Type>(opcode), instr);
780     }
781 }
782
783 void QV4CompilerPrivate::visitCall(IR::Call *call)
784 {
785     if (IR::Name *name = call->base->asName()) {
786         IR::Expr *arg = call->onlyArgument();
787         if (arg != 0 && arg->type == IR::RealType) {
788             traceExpression(arg, currentReg);
789
790             switch (name->builtin) {
791             case IR::NoBuiltinSymbol:
792                 break;
793
794             case IR::MathSinBultinFunction: {
795                 Instr::MathSinReal i;
796                 i.output = i.src = currentReg;
797                 gen(i);
798                 } return;
799
800             case IR::MathCosBultinFunction: {
801                 Instr::MathCosReal i;
802                 i.output = i.src = currentReg;
803                 gen(i);
804                 } return;
805
806             case IR::MathRoundBultinFunction: {
807                 Instr::MathRoundReal i;
808                 i.output = i.src = currentReg;
809                 gen(i);
810                 } return;
811
812             case IR::MathFloorBultinFunction: {
813                 Instr::MathFloorReal i;
814                 i.output = i.src = currentReg;
815                 gen(i);
816                 } return;
817
818             case IR::MathPIBuiltinConstant:
819                 break;
820             } // switch
821         }
822     }
823
824     if (qmlVerboseCompiler())
825         qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__;
826     discard();
827 }
828
829
830 //
831 // statements
832 //
833 void QV4CompilerPrivate::visitExp(IR::Exp *s)
834 {
835     traceExpression(s->expr, currentReg);
836 }
837
838 void QV4CompilerPrivate::visitMove(IR::Move *s)
839 {
840     IR::Temp *target = s->target->asTemp();
841     Q_ASSERT(target != 0);
842
843     quint8 dest = target->index;
844
845     if (target->type != s->source->type) {
846         quint8 src = dest;
847
848         if (IR::Temp *t = s->source->asTemp()) 
849             src = t->index;
850         else
851             traceExpression(s->source, dest);
852
853         V4Instr::Type opcode = V4Instr::Noop;
854         IR::Type targetTy = s->target->type;
855         IR::Type sourceTy = s->source->type;
856
857         if (sourceTy == IR::UrlType) {
858             switch (targetTy) {
859             case IR::BoolType:
860             case IR::StringType:
861                 // nothing to do. V4 will generate optimized
862                 // url-to-xxx conversions.
863                 break;
864             default: {
865                 // generate a UrlToString conversion and fix
866                 // the type of the source expression.
867                 V4Instr conv;
868                 conv.unaryop.output = V4Instr::ConvertUrlToString;
869                 conv.unaryop.src = src;
870                 gen(opcode, conv);
871
872                 sourceTy = IR::StringType;
873                 break;
874             }
875             } // switch
876         }
877
878         if (targetTy == IR::BoolType) {
879             switch (sourceTy) {
880             case IR::IntType: opcode = V4Instr::ConvertIntToBool; break;
881             case IR::RealType: opcode = V4Instr::ConvertRealToBool; break;
882             case IR::StringType: opcode = V4Instr::ConvertStringToBool; break;
883             case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break;
884             default: break;
885             } // switch
886         } else if (targetTy == IR::IntType) {
887             switch (sourceTy) {
888             case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break;
889             case IR::RealType: {
890                 if (s->isMoveForReturn)
891                     opcode = V4Instr::MathRoundReal;
892                 else
893                     opcode = V4Instr::ConvertRealToInt;
894                 break;
895             }
896             case IR::StringType: opcode = V4Instr::ConvertStringToInt; break;
897             default: break;
898             } // switch
899         } else if (targetTy == IR::RealType) {
900             switch (sourceTy) {
901             case IR::BoolType: opcode = V4Instr::ConvertBoolToReal; break;
902             case IR::IntType: opcode = V4Instr::ConvertIntToReal; break;
903             case IR::StringType: opcode = V4Instr::ConvertStringToReal; break;
904             default: break;
905             } // switch
906         } else if (targetTy == IR::StringType) {
907             switch (sourceTy) {
908             case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break;
909             case IR::IntType:  opcode = V4Instr::ConvertIntToString; break;
910             case IR::RealType: opcode = V4Instr::ConvertRealToString; break;
911             case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break;
912             default: break;
913             } // switch
914         } else if (targetTy == IR::UrlType) {
915             V4Instr convToString;
916             convToString.unaryop.output = dest;
917             convToString.unaryop.src = src;
918
919             // try to convert the source expression to a string.
920             switch (sourceTy) {
921             case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break;
922             case IR::IntType:  gen(V4Instr::ConvertIntToString,  convToString); sourceTy = IR::StringType; break;
923             case IR::RealType: gen(V4Instr::ConvertRealToString, convToString); sourceTy = IR::StringType; break;
924             default: break;
925             } // switch
926
927             if (sourceTy == IR::StringType)
928                 opcode = V4Instr::ConvertStringToUrl;
929         }
930         if (opcode != V4Instr::Noop) {
931             V4Instr conv;
932             conv.unaryop.output = dest;
933             conv.unaryop.src = src;
934             gen(opcode, conv);
935
936             if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) {
937                 V4Instr resolveUrl;
938                 resolveUrl.unaryop.output = dest;
939                 resolveUrl.unaryop.src = dest;
940                 gen(V4Instr::ResolveUrl, resolveUrl);
941             }
942         } else {
943             discard();
944         }
945     } else {
946         traceExpression(s->source, dest);
947     }
948 }
949
950 void QV4CompilerPrivate::visitJump(IR::Jump *s)
951 {
952     patches.append(Patch(s->target, bytecode.size()));
953
954     Instr::Branch i;
955     i.offset = 0; // ### backpatch
956     gen(i);
957 }
958
959 void QV4CompilerPrivate::visitCJump(IR::CJump *s)
960 {
961     traceExpression(s->cond, currentReg);
962
963     patches.append(Patch(s->iftrue, bytecode.size()));
964
965     Instr::BranchTrue i;
966     i.reg = currentReg;
967     i.offset = 0; // ### backpatch
968     gen(i);
969 }
970
971 void QV4CompilerPrivate::visitRet(IR::Ret *s)
972 {
973     Q_ASSERT(s->expr != 0);
974
975     int storeReg = currentReg;
976
977     if (IR::Temp *temp = s->expr->asTemp()) {
978         storeReg = temp->index;
979     } else {
980         traceExpression(s->expr, storeReg);
981     }
982
983     if (qmlBindingsTest) {
984         Instr::TestV4Store test;
985         test.reg = storeReg;
986         switch (s->type) {
987         case IR::StringType:
988             test.regType = QMetaType::QString;
989             break;
990         case IR::UrlType:
991             test.regType = QMetaType::QUrl;
992             break;
993         case IR::AnchorLineType:
994             test.regType = qMetaTypeId<QDeclarative1AnchorLine>();
995             break;
996         case IR::SGAnchorLineType:
997             test.regType = QDeclarativeMetaType::QQuickAnchorLineMetaTypeId();
998             break;
999         case IR::ObjectType:
1000             test.regType = QMetaType::QObjectStar;
1001             break;
1002         case IR::BoolType:
1003             test.regType = QMetaType::Bool;
1004             break;
1005         case IR::IntType:
1006             test.regType = QMetaType::Int;
1007             break;
1008         case IR::RealType:
1009             test.regType = QMetaType::QReal;
1010             break;
1011         default:
1012             discard();
1013             return;
1014         }
1015         gen(test);
1016     }
1017
1018     Instr::Store store;
1019     store.output = 0;
1020     store.index = expression->property->index;
1021     store.reg = storeReg;
1022     store.exceptionId = exceptionId(s->line, s->column);
1023     gen(store);
1024 }
1025
1026 void QV4Compiler::dump(const QByteArray &programData)
1027 {
1028     const QV4Program *program = (const QV4Program *)programData.constData();
1029
1030     qWarning() << "Program.bindings:" << program->bindings;
1031     qWarning() << "Program.dataLength:" << program->dataLength;
1032     qWarning() << "Program.subscriptions:" << program->subscriptions;
1033     qWarning() << "Program.indentifiers:" << program->identifiers;
1034
1035     const int programSize = program->instructionCount;
1036     const char *start = program->instructions();
1037     const char *end = start + programSize;
1038     Bytecode bc;
1039     bc.dump(start, end);
1040 }
1041
1042 /*!
1043 Clear the state associated with attempting to compile a specific binding.
1044 This does not clear the global "committed binding" states.
1045 */
1046 void QV4CompilerPrivate::resetInstanceState()
1047 {
1048     data = committed.data;
1049     exceptions = committed.exceptions;
1050     usedSubscriptionIds.clear();
1051     subscriptionIds = committed.subscriptionIds;
1052     registeredStrings = committed.registeredStrings;
1053     bytecode.clear();
1054     patches.clear();
1055     pool.clear();
1056     currentReg = 0;
1057 }
1058
1059 /*!
1060 Mark the last compile as successful, and add it to the "committed data"
1061 section.
1062
1063 Returns the index for the committed binding.
1064 */
1065 int QV4CompilerPrivate::commitCompile()
1066 {
1067     int rv = committed.count();
1068     committed.offsets << committed.bytecode.count();
1069     committed.dependencies << usedSubscriptionIds;
1070     committed.bytecode.append(bytecode.constData(), bytecode.size());
1071     committed.data = data;
1072     committed.exceptions = exceptions;
1073     committed.subscriptionIds = subscriptionIds;
1074     committed.registeredStrings = registeredStrings;
1075     return rv;
1076 }
1077
1078 bool QV4CompilerPrivate::compile(QDeclarativeJS::AST::Node *node)
1079 {
1080     resetInstanceState();
1081
1082     if (expression->property->type == -1)
1083         return false;
1084
1085     AST::SourceLocation location;
1086     if (AST::ExpressionNode *astExpression = node->expressionCast()) {
1087         location = astExpression->firstSourceLocation();
1088     } else if (AST::Statement *astStatement = node->statementCast()) {
1089         if (AST::Block *block = AST::cast<AST::Block *>(astStatement))
1090             location = block->lbraceToken;
1091         else if (AST::IfStatement *ifStmt = AST::cast<AST::IfStatement *>(astStatement))
1092             location = ifStmt->ifToken;
1093         else
1094             return false;
1095     } else {
1096         return false;
1097     }
1098
1099     IR::Function thisFunction(&pool), *function = &thisFunction;
1100
1101     QV4IRBuilder irBuilder(expression, engine);
1102     if (!irBuilder(function, node))
1103         return false;
1104
1105     bool discarded = false;
1106     qSwap(_discarded, discarded);
1107     qSwap(_function, function);
1108     trace(location.startLine, location.startColumn);
1109     qSwap(_function, function);
1110     qSwap(_discarded, discarded);
1111
1112     if (qmlVerboseCompiler()) {
1113         QTextStream qerr(stderr, QIODevice::WriteOnly);
1114         if (discarded)
1115             qerr << "======== TODO ====== " << endl;
1116         else 
1117             qerr << "==================== " << endl;
1118         qerr << "\tline: " << location.startLine
1119              << "\tcolumn: " << location.startColumn
1120              << endl;
1121         foreach (IR::BasicBlock *bb, function->basicBlocks)
1122             bb->dump(qerr);
1123         qerr << endl;
1124     }
1125
1126     if (discarded || subscriptionIds.count() > 0xFFFF || registeredStrings.count() > 0xFFFF)
1127         return false;
1128
1129     return true;
1130 }
1131
1132 // Returns a reg
1133 int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str)
1134 {
1135     // ### string cleanup
1136
1137     QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
1138     int offset = data.count();
1139     data += strdata;
1140
1141     Instr::LoadString string;
1142     string.reg = reg;
1143     string.offset = offset;
1144     string.length = str.length();
1145     gen(string);
1146
1147     return reg;
1148 }
1149
1150 // Returns an identifier offset
1151 int QV4CompilerPrivate::registerString(const QString &string)
1152 {
1153     Q_ASSERT(!string.isEmpty());
1154
1155     QPair<int, int> *iter = registeredStrings.value(string);
1156
1157     if (!iter) {
1158         quint32 len = string.length();
1159         QByteArray lendata((const char *)&len, sizeof(quint32));
1160         QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar));
1161         strdata.prepend(lendata);
1162         int rv = data.count();
1163         data += strdata;
1164
1165         iter = &registeredStrings[string];
1166         *iter = qMakePair(registeredStrings.count(), rv);
1167     } 
1168
1169     Instr::InitString reg;
1170     reg.offset = iter->first;
1171     reg.dataIdx = iter->second;
1172     gen(reg);
1173     return reg.offset;
1174 }
1175
1176 /*!
1177 Returns true if the current expression has not already subscribed to \a sub in currentBlockMask.
1178 */
1179 bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub)
1180 {
1181     QString str = sub.join(QLatin1String("."));
1182
1183     int *iter = subscriptionIds.value(str);
1184     if (!iter)
1185         return true;
1186
1187     quint32 *uiter = usedSubscriptionIds.value(*iter);
1188     if (!uiter)
1189         return true;
1190     else
1191         return !(*uiter & currentBlockMask);
1192 }
1193
1194 int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub)
1195 {
1196     QString str = sub.join(QLatin1String("."));
1197     int *iter = subscriptionIds.value(str);
1198     if (!iter) {
1199         int count = subscriptionIds.count();
1200         iter = &subscriptionIds[str];
1201         *iter = count;
1202     }
1203     quint32 &u = usedSubscriptionIds[*iter];
1204     if (!(u & currentBlockMask)) {
1205         u |= currentBlockMask;
1206         usedSubscriptionIdsChanged = true;
1207     }
1208     return *iter;
1209 }
1210
1211 quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub)
1212 {
1213     QString str = sub.join(QLatin1String("."));
1214
1215     int *iter = subscriptionIds.value(str);
1216     Q_ASSERT(iter != 0);
1217
1218     quint32 *uiter = usedSubscriptionIds.value(*iter);
1219     Q_ASSERT(uiter != 0);
1220
1221     return *uiter;
1222 }
1223
1224 quint8 QV4CompilerPrivate::exceptionId(quint32 line, quint32 column)
1225 {
1226     quint8 rv = 0xFF;
1227     if (exceptions.count() < 0xFF) {
1228         rv = (quint8)exceptions.count();
1229         quint64 e = line;
1230         e <<= 32;
1231         e |= column;
1232         exceptions.append(e);
1233     }
1234     return rv;
1235 }
1236
1237 quint8 QV4CompilerPrivate::exceptionId(QDeclarativeJS::AST::ExpressionNode *n)
1238 {
1239     quint8 rv = 0xFF;
1240     if (n && exceptions.count() < 0xFF) {
1241         QDeclarativeJS::AST::SourceLocation l = n->firstSourceLocation();
1242         rv = exceptionId(l.startLine, l.startColumn);
1243     }
1244     return rv;
1245 }
1246
1247 QV4Compiler::QV4Compiler()
1248 : d(new QV4CompilerPrivate)
1249 {
1250     qmlBindingsTest |= qmlBindingsTestEnv();
1251 }
1252
1253 QV4Compiler::~QV4Compiler()
1254 {
1255     delete d; d = 0;
1256 }
1257
1258 /* 
1259 Returns true if any bindings were compiled.
1260 */
1261 bool QV4Compiler::isValid() const
1262 {
1263     return !d->committed.bytecode.isEmpty();
1264 }
1265
1266 /* 
1267 -1 on failure, otherwise the binding index to use.
1268 */
1269 int QV4Compiler::compile(const Expression &expression, QDeclarativeEnginePrivate *engine)
1270 {
1271     if (!expression.expression.asAST()) return false;
1272
1273     if (!qmlExperimental() && expression.property->isValueTypeSubProperty)
1274         return -1;
1275
1276     if (qmlDisableOptimizer() || !qmlEnableV4)
1277         return -1;
1278
1279     d->expression = &expression;
1280     d->engine = engine;
1281
1282     if (d->compile(expression.expression.asAST())) {
1283         return d->commitCompile();
1284     } else {
1285         return -1;
1286     }
1287 }
1288
1289 QByteArray QV4CompilerPrivate::buildSignalTable() const
1290 {
1291     QHash<int, QList<QPair<int, quint32> > > table;
1292
1293     for (int ii = 0; ii < committed.count(); ++ii) {
1294         const QDeclarativeAssociationList<int, quint32> &deps = committed.dependencies.at(ii);
1295         for (QDeclarativeAssociationList<int, quint32>::const_iterator iter = deps.begin(); iter != deps.end(); ++iter)
1296             table[iter->first].append(qMakePair(ii, iter->second));
1297     }
1298
1299     QVector<quint32> header;
1300     QVector<quint32> data;
1301     for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) {
1302         header.append(committed.subscriptionIds.count() + data.count());
1303         const QList<QPair<int, quint32> > &bindings = table[ii];
1304         data.append(bindings.count());
1305         for (int jj = 0; jj < bindings.count(); ++jj) {
1306             data.append(bindings.at(jj).first);
1307             data.append(bindings.at(jj).second);
1308         }
1309     }
1310     header << data;
1311
1312     return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32));
1313 }
1314
1315 QByteArray QV4CompilerPrivate::buildExceptionData() const
1316 {
1317     QByteArray rv;
1318     rv.resize(committed.exceptions.count() * sizeof(quint64));
1319     ::memcpy(rv.data(), committed.exceptions.constData(), rv.size());
1320     return rv;
1321 }
1322
1323 /* 
1324 Returns the compiled program.
1325 */
1326 QByteArray QV4Compiler::program() const
1327 {
1328     QByteArray programData;
1329
1330     if (isValid()) {
1331         QV4Program prog;
1332         prog.bindings = d->committed.count();
1333
1334         Bytecode bc;
1335         QV4CompilerPrivate::Instr::Jump jump;
1336         jump.reg = -1;
1337
1338         for (int ii = 0; ii < d->committed.count(); ++ii) {
1339             jump.count = d->committed.count() - ii - 1;
1340             jump.count*= V4InstrMeta<V4Instr::Jump>::Size;
1341             jump.count+= d->committed.offsets.at(ii);
1342             bc.append(jump);
1343         }
1344
1345
1346         QByteArray bytecode;
1347         bytecode.reserve(bc.size() + d->committed.bytecode.size());
1348         bytecode.append(bc.constData(), bc.size());
1349         bytecode.append(d->committed.bytecode.constData(), d->committed.bytecode.size());
1350
1351         QByteArray data = d->committed.data;
1352         while (data.count() % 4) data.append('\0');
1353         prog.signalTableOffset = data.count();
1354         data += d->buildSignalTable();
1355         while (data.count() % 4) data.append('\0');
1356         prog.exceptionDataOffset = data.count();
1357         data += d->buildExceptionData();
1358
1359         prog.dataLength = 4 * ((data.size() + 3) / 4);
1360         prog.subscriptions = d->committed.subscriptionIds.count();
1361         prog.identifiers = d->committed.registeredStrings.count();
1362         prog.instructionCount = bytecode.count();
1363         int size = sizeof(QV4Program) + bytecode.count();
1364         size += prog.dataLength;
1365
1366         programData.resize(size);
1367         memcpy(programData.data(), &prog, sizeof(QV4Program));
1368         if (prog.dataLength)
1369             memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(), 
1370                    data.size());
1371         memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(),
1372                bytecode.count());
1373     } 
1374
1375     if (bindingsDump()) {
1376         qWarning().nospace() << "Subscription slots:";
1377
1378         for (QDeclarativeAssociationList<QString, int>::ConstIterator iter = d->committed.subscriptionIds.begin();
1379                 iter != d->committed.subscriptionIds.end();
1380                 ++iter) {
1381             qWarning().nospace() << "    " << iter->first << "\t-> " << iter->second;
1382         }
1383
1384         QV4Compiler::dump(programData);
1385     }
1386
1387     return programData;
1388 }
1389
1390 void QV4Compiler::enableBindingsTest(bool e)
1391 {
1392     if (e)
1393         qmlBindingsTest = true;
1394     else 
1395         qmlBindingsTest = qmlBindingsTestEnv();
1396 }
1397
1398 void QV4Compiler::enableV4(bool e)
1399 {
1400     qmlEnableV4 = e;
1401 }
1402
1403 QT_END_NAMESPACE