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