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