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