1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
44 #include <QtCore/qtextstream.h>
45 #include <QtCore/qdebug.h>
50 namespace QDeclarativeJS {
53 inline const char *typeName(Type t)
56 case InvalidType: return "invalid";
57 case UndefinedType: return "undefined";
58 case NullType: return "null";
59 case VoidType: return "void";
60 case StringType: return "string";
61 case UrlType: return "url";
62 case AnchorLineType: return "AnchorLine";
63 case SGAnchorLineType: return "SGAnchorLine";
64 case AttachType: return "AttachType";
65 case ObjectType: return "object";
66 case BoolType: return "bool";
67 case IntType: return "int";
68 case RealType: return "qreal";
69 case RealNaNType: return "NaN";
70 default: return "invalid";
74 inline bool isNumberType(IR::Type ty)
76 return ty >= IR::FirstNumberType;
79 inline bool isStringType(IR::Type ty)
81 return ty == IR::StringType || ty == IR::UrlType;
84 IR::Type maxType(IR::Type left, IR::Type right)
86 if (isStringType(left) && isStringType(right)) {
87 // String promotions (url to string) are more specific than
88 // identity conversions (AKA left == right). That's because
89 // we want to ensure we convert urls to strings in binary
91 return IR::StringType;
92 } else if (left == right)
94 else if (isNumberType(left) && isNumberType(right))
95 return qMax(left, right);
96 else if ((isNumberType(left) && isStringType(right)) ||
97 (isNumberType(right) && isStringType(left)))
98 return IR::StringType;
100 return IR::InvalidType;
104 const char *opname(AluOp op)
107 case OpInvalid: return "?";
109 case OpIfTrue: return "(bool)";
110 case OpNot: return "!";
111 case OpUMinus: return "-";
112 case OpUPlus: return "+";
113 case OpCompl: return "~";
115 case OpBitAnd: return "&";
116 case OpBitOr: return "|";
117 case OpBitXor: return "^";
119 case OpAdd: return "+";
120 case OpSub: return "-";
121 case OpMul: return "*";
122 case OpDiv: return "/";
123 case OpMod: return "%";
125 case OpLShift: return "<<";
126 case OpRShift: return ">>";
127 case OpURShift: return ">>>";
129 case OpGt: return ">";
130 case OpLt: return "<";
131 case OpGe: return ">=";
132 case OpLe: return "<=";
133 case OpEqual: return "==";
134 case OpNotEqual: return "!=";
135 case OpStrictEqual: return "===";
136 case OpStrictNotEqual: return "!==";
138 case OpAnd: return "&&";
139 case OpOr: return "||";
146 AluOp binaryOperator(int op)
148 switch (static_cast<QSOperator::Op>(op)) {
149 case QSOperator::Add: return OpAdd;
150 case QSOperator::And: return OpAnd;
151 case QSOperator::BitAnd: return OpBitAnd;
152 case QSOperator::BitOr: return OpBitOr;
153 case QSOperator::BitXor: return OpBitXor;
154 case QSOperator::Div: return OpDiv;
155 case QSOperator::Equal: return OpEqual;
156 case QSOperator::Ge: return OpGe;
157 case QSOperator::Gt: return OpGt;
158 case QSOperator::Le: return OpLe;
159 case QSOperator::LShift: return OpLShift;
160 case QSOperator::Lt: return OpLt;
161 case QSOperator::Mod: return OpMod;
162 case QSOperator::Mul: return OpMul;
163 case QSOperator::NotEqual: return OpNotEqual;
164 case QSOperator::Or: return OpOr;
165 case QSOperator::RShift: return OpRShift;
166 case QSOperator::StrictEqual: return OpStrictEqual;
167 case QSOperator::StrictNotEqual: return OpStrictNotEqual;
168 case QSOperator::Sub: return OpSub;
169 case QSOperator::URShift: return OpURShift;
170 default: return OpInvalid;
174 void Const::dump(QTextStream &out)
179 void String::dump(QTextStream &out)
181 out << '"' << escape(value) << '"';
184 QString String::escape(const QStringRef &s)
187 for (int i = 0; i < s.length(); ++i) {
188 const QChar ch = s.at(i);
189 if (ch == QLatin1Char('\n'))
190 r += QLatin1String("\\n");
191 else if (ch == QLatin1Char('\r'))
192 r += QLatin1String("\\r");
193 else if (ch == QLatin1Char('\\'))
194 r += QLatin1String("\\\\");
195 else if (ch == QLatin1Char('"'))
196 r += QLatin1String("\\\"");
197 else if (ch == QLatin1Char('\''))
198 r += QLatin1String("\\'");
205 void Name::init(Name *base, Type type, const QString *id, Symbol symbol, quint32 line, quint32 column)
210 this->symbol = symbol;
213 this->storage = MemberStorage;
214 this->builtin = NoBuiltinSymbol;
216 this->column = column;
218 if (id->length() == 8 && *id == QLatin1String("Math.sin")) {
219 builtin = MathSinBultinFunction;
220 } else if (id->length() == 8 && *id == QLatin1String("Math.cos")) {
221 builtin = MathCosBultinFunction;
222 } else if (id->length() == 10 && *id == QLatin1String("Math.round")) {
223 builtin = MathRoundBultinFunction;
224 } else if (id->length() == 10 && *id == QLatin1String("Math.floor)")) {
225 builtin = MathFloorBultinFunction;
226 } else if (id->length() == 7 && *id == QLatin1String("Math.PI")) {
227 builtin = MathPIBuiltinConstant;
228 this->type = RealType;
232 void Name::dump(QTextStream &out)
242 void Temp::dump(QTextStream &out)
247 void Unop::dump(QTextStream &out)
253 Type Unop::typeForOp(AluOp op, Expr *expr)
256 case OpIfTrue: return BoolType;
257 case OpNot: return BoolType;
262 return maxType(expr->type, RealType);
271 void Binop::dump(QTextStream &out)
274 out << ' ' << opname(op) << ' ';
278 Type Binop::typeForOp(AluOp op, Expr *left, Expr *right)
280 if (! (left && right))
302 if (left->type == StringType)
328 case OpStrictNotEqual:
335 void Call::dump(QTextStream &out)
339 for (ExprList *it = args; it; it = it->next) {
347 Type Call::typeForFunction(Expr *base)
352 if (Name *name = base->asName()) {
353 switch (name->builtin) {
354 case MathSinBultinFunction:
355 case MathCosBultinFunction:
358 case MathRoundBultinFunction:
359 case MathFloorBultinFunction:
362 case NoBuiltinSymbol:
363 case MathPIBuiltinConstant:
371 void Exp::dump(QTextStream &out, Mode)
378 void Move::dump(QTextStream &out, Mode)
382 if (source->type != target->type)
383 out << typeName(source->type) << "_to_" << typeName(target->type) << '(';
385 if (source->type != target->type)
390 void Jump::dump(QTextStream &out, Mode mode)
393 out << "goto " << 'L' << target << ';';
396 void CJump::dump(QTextStream &out, Mode mode)
401 out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';';
404 void Ret::dump(QTextStream &out, Mode)
414 Function::~Function()
416 qDeleteAll(basicBlocks);
419 QString *Function::newString(const QString &text)
421 return pool->NewString(text);
424 BasicBlock *Function::newBasicBlock()
426 const int index = basicBlocks.size();
427 return i(new BasicBlock(this, index));
430 void Function::dump(QTextStream &out)
432 out << "function () {" << endl;
433 foreach (BasicBlock *bb, basicBlocks) {
439 Temp *BasicBlock::TEMP(Type type, int index)
441 Temp *e = function->pool->New<Temp>();
442 e->init(type, index);
446 Temp *BasicBlock::TEMP(Type type)
448 return TEMP(type, function->tempCount++);
451 Expr *BasicBlock::CONST(double value)
453 return CONST(IR::RealType, value);
456 Expr *BasicBlock::CONST(Type type, double value)
458 Const *e = function->pool->New<Const>();
459 e->init(type, value);
463 Expr *BasicBlock::STRING(const QStringRef &value)
465 String *e = function->pool->New<String>();
470 Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
472 return NAME(0, id, line, column);
475 Name *BasicBlock::NAME(Name *base, const QString &id, quint32 line, quint32 column)
477 Name *e = function->pool->New<Name>();
478 e->init(base, InvalidType,
479 function->newString(id),
480 Name::Unbound, line, column);
484 Name *BasicBlock::SYMBOL(Type type, const QString &id, const QMetaObject *meta, QDeclarativePropertyData *property, Name::Storage storage,
485 quint32 line, quint32 column)
487 Name *name = SYMBOL(/*base = */ 0, type, id, meta, property, line, column);
488 name->storage = storage;
492 Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QDeclarativePropertyData *property, Name::Storage storage,
493 quint32 line, quint32 column)
495 Name *name = function->pool->New<Name>();
496 name->init(base, type, function->newString(id),
497 Name::Property, line, column);
499 name->property = property;
500 name->storage = storage;
504 Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QMetaObject *meta, QDeclarativePropertyData *property,
505 quint32 line, quint32 column)
507 Name *name = function->pool->New<Name>();
508 name->init(base, type, function->newString(id),
509 Name::Property, line, column);
511 name->property = property;
515 Name *BasicBlock::ID_OBJECT(const QString &id, const QDeclarativeScript::Object *object, quint32 line, quint32 column)
517 Name *name = function->pool->New<Name>();
518 name->init(/*base = */ 0, IR::ObjectType,
519 function->newString(id),
520 Name::IdObject, line, column);
521 name->idObject = object;
523 name->storage = Name::IdStorage;
527 Name *BasicBlock::ATTACH_TYPE(const QString &id, const QDeclarativeType *attachType, Name::Storage storage,
528 quint32 line, quint32 column)
530 Name *name = function->pool->New<Name>();
531 name->init(/*base = */ 0, IR::AttachType,
532 function->newString(id),
533 Name::AttachType, line, column);
534 name->declarativeType = attachType;
535 name->storage = storage;
540 Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
542 Unop *e = function->pool->New<Unop>();
547 Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
550 if (Const *c1 = left->asConst()) {
551 if (Const *c2 = right->asConst()) {
553 case OpAdd: return CONST(c1->value + c2->value);
554 case OpAnd: return CONST(c1->value ? c2->value : 0);
555 case OpBitAnd: return CONST(int(c1->value) & int(c2->value));
556 case OpBitOr: return CONST(int(c1->value) | int(c2->value));
557 case OpBitXor: return CONST(int(c1->value) ^ int(c2->value));
558 case OpDiv: return CONST(c1->value / c2->value);
559 case OpEqual: return CONST(c1->value == c2->value);
560 case OpGe: return CONST(c1->value >= c2->value);
561 case OpGt: return CONST(c1->value > c2->value);
562 case OpLe: return CONST(c1->value <= c2->value);
563 case OpLShift: return CONST(int(c1->value) << int(c2->value));
564 case OpLt: return CONST(c1->value < c2->value);
565 case OpMod: return CONST(::fmod(c1->value, c2->value));
566 case OpMul: return CONST(c1->value * c2->value);
567 case OpNotEqual: return CONST(c1->value != c2->value);
568 case OpOr: return CONST(c1->value ? c1->value : c2->value);
569 case OpRShift: return CONST(int(c1->value) >> int(c2->value));
570 case OpStrictEqual: return CONST(c1->value == c2->value);
571 case OpStrictNotEqual: return CONST(c1->value != c2->value);
572 case OpSub: return CONST(c1->value - c2->value);
573 case OpURShift: return CONST(unsigned(c1->value) >> int(c2->value));
575 case OpIfTrue: // unary ops
587 Binop *e = function->pool->New<Binop>();
588 e->init(op, left, right);
592 Expr *BasicBlock::CALL(Expr *base, ExprList *args)
594 Call *e = function->pool->New<Call>();
599 Stmt *BasicBlock::EXP(Expr *expr)
601 Exp *s = function->pool->New<Exp>();
603 statements.append(s);
607 Stmt *BasicBlock::MOVE(Expr *target, Expr *source, bool isMoveForReturn)
609 Move *s = function->pool->New<Move>();
610 s->init(target, source, isMoveForReturn);
611 statements.append(s);
615 Stmt *BasicBlock::JUMP(BasicBlock *target)
620 Jump *s = function->pool->New<Jump>();
622 statements.append(s);
626 Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
631 CJump *s = function->pool->New<CJump>();
632 s->init(cond, iftrue, iffalse);
633 statements.append(s);
637 Stmt *BasicBlock::RET(Expr *expr, Type type, quint32 line, quint32 column)
642 Ret *s = function->pool->New<Ret>();
643 s->init(expr, type, line, column);
644 statements.append(s);
648 void BasicBlock::dump(QTextStream &out)
650 out << 'L' << this << ':' << endl;
651 foreach (Stmt *s, statements) {
658 #ifdef DEBUG_IR_STRUCTURE
660 static const char *symbolname(Name::Symbol s)
667 case Name::AttachType:
676 Q_ASSERT(!"Unreachable");
681 static const char *storagename(Name::Storage s)
684 case Name::MemberStorage:
685 return "MemberStorage";
686 case Name::IdStorage:
688 case Name::RootStorage:
689 return "RootStorage";
690 case Name::ScopeStorage:
691 return "ScopeStorage";
693 Q_ASSERT(!"Unreachable");
694 return "UnknownStorage";
706 indentData = QByteArray(indentSize * 4, ' ');
712 indentData = QByteArray(indentSize * 4, ' ');
717 void IRDump::expression(QDeclarativeJS::IR::Expr *e)
726 void IRDump::basicblock(QDeclarativeJS::IR::BasicBlock *b)
730 qWarning().nospace() << indent() << "BasicBlock " << b << " {";
731 for (int ii = 0; ii < b->statements.count(); ++ii) {
732 statement(b->statements.at(ii));
733 if (ii != (b->statements.count() - 1))
736 qWarning().nospace() << indent() << "}";
741 void IRDump::statement(QDeclarativeJS::IR::Stmt *s)
750 void IRDump::function(QDeclarativeJS::IR::Function *f)
754 qWarning().nospace() << indent() << "Function {";
755 for (int ii = 0; ii < f->basicBlocks.count(); ++ii) {
756 basicblock(f->basicBlocks.at(ii));
758 qWarning().nospace() << indent() << "}";
763 const char *IRDump::indent()
765 return indentData.constData();
768 void IRDump::visitConst(QDeclarativeJS::IR::Const *e)
770 qWarning().nospace() << indent() << "Const:Expr { type: " << typeName(e->type) << ", value: " << e->value << "}";
773 void IRDump::visitString(QDeclarativeJS::IR::String *e)
775 qWarning().nospace() << indent() << "String:Expr { type: " << typeName(e->type) << ", value: " << e->value << "}";
778 static void namedumprecur(QDeclarativeJS::IR::Name *e, const char *indent)
780 if (e->base) namedumprecur(e->base, indent);
781 qWarning().nospace() << indent << " { type: " << typeName(e->type) << ", symbol: " << symbolname(e->symbol) << ", storage: " << storagename(e->storage) << ", id: " << e->id << "}";
784 void IRDump::visitName(QDeclarativeJS::IR::Name *e)
786 qWarning().nospace() << indent() << "Name:Expr {";
787 namedumprecur(e, indent());
788 qWarning().nospace() << indent() << "}";
791 void IRDump::visitTemp(QDeclarativeJS::IR::Temp *e)
793 qWarning().nospace() << indent() << "Temp:Expr { type: " << typeName(e->type) << ", index: " << e->index << " }";
796 void IRDump::visitUnop(QDeclarativeJS::IR::Unop *e)
798 qWarning().nospace() << indent() << "Unop:Expr { ";
799 qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op);
800 qWarning().nospace() << indent() << " expr: {";
802 qWarning().nospace() << indent() << " }";
803 qWarning().nospace() << indent() << "}";
806 void IRDump::visitBinop(QDeclarativeJS::IR::Binop *e)
808 qWarning().nospace() << indent() << "Binop:Expr { ";
809 qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op);
810 qWarning().nospace() << indent() << " left: {";
814 qWarning().nospace() << indent() << " },";
815 qWarning().nospace() << indent() << " right: {";
817 expression(e->right);
819 qWarning().nospace() << indent() << " }";
820 qWarning().nospace() << indent() << "}";
823 void IRDump::visitCall(QDeclarativeJS::IR::Call *e)
826 qWarning().nospace() << indent() << "Exp::Call { }";
829 void IRDump::visitExp(QDeclarativeJS::IR::Exp *s)
831 qWarning().nospace() << indent() << "Exp:Stmt {";
833 qWarning().nospace() << indent() << "}";
836 void IRDump::visitMove(QDeclarativeJS::IR::Move *s)
838 qWarning().nospace() << indent() << "Move:Stmt {";
839 qWarning().nospace() << indent() << " isMoveForReturn: " << s->isMoveForReturn;
840 qWarning().nospace() << indent() << " target: {";
842 expression(s->target);
844 qWarning().nospace() << indent() << " },";
845 qWarning().nospace() << indent() << " source: {";
847 expression(s->source);
849 qWarning().nospace() << indent() << " }";
850 qWarning().nospace() << indent() << "}";
853 void IRDump::visitJump(QDeclarativeJS::IR::Jump *s)
855 qWarning().nospace() << indent() << "Jump:Stmt { BasicBlock(" << s->target << ") }";
858 void IRDump::visitCJump(QDeclarativeJS::IR::CJump *s)
860 qWarning().nospace() << indent() << "CJump:Stmt {";
861 qWarning().nospace() << indent() << " cond: {";
865 qWarning().nospace() << indent() << " }";
866 qWarning().nospace() << indent() << " iftrue: BasicBlock(" << s->iftrue << ")";
867 qWarning().nospace() << indent() << " iffalse: BasicBlock(" << s->iffalse << ")";
868 qWarning().nospace() << indent() << "}";
871 void IRDump::visitRet(QDeclarativeJS::IR::Ret *s)
873 qWarning().nospace() << indent() << "Ret:Stmt {";
874 qWarning().nospace() << indent() << " type: " << typeName(s->type);
876 qWarning().nospace() << indent() << "}";
880 } // end of namespace IR
881 } // end of namespace QDeclarativeJS