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