698 lines
17 KiB
C++
698 lines
17 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/
|
|
**
|
|
** This file is part of the QtQml module of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** GNU Lesser General Public License Usage
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU Lesser
|
|
** General Public License version 2.1 requirements will be met:
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
|
** Public License version 3.0 as published by the Free Software Foundation
|
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU General
|
|
** Public License version 3.0 requirements will be met:
|
|
** http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
** Other Usage
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qv4ir_p.h"
|
|
#include <private/qqmljsast_p.h>
|
|
|
|
#include <QtCore/qtextstream.h>
|
|
#include <QtCore/qdebug.h>
|
|
#include <math.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
namespace QQmlJS {
|
|
namespace IR {
|
|
|
|
const char *typeName(Type t)
|
|
{
|
|
switch (t) {
|
|
case InvalidType: return "invalid";
|
|
case UndefinedType: return "undefined";
|
|
case NullType: return "null";
|
|
case VoidType: return "void";
|
|
case StringType: return "string";
|
|
case UrlType: return "QUrl";
|
|
case ColorType: return "QColor";
|
|
case SGAnchorLineType: return "SGAnchorLine";
|
|
case AttachType: return "AttachType";
|
|
case ObjectType: return "object";
|
|
case VariantType: return "variant";
|
|
case VarType: return "var";
|
|
case BoolType: return "bool";
|
|
case IntType: return "int";
|
|
case FloatType: return "float";
|
|
case NumberType: return "number";
|
|
default: return "invalid";
|
|
}
|
|
}
|
|
|
|
inline bool isNumberType(IR::Type ty)
|
|
{
|
|
return ty >= IR::FirstNumberType;
|
|
}
|
|
|
|
inline bool isStringType(IR::Type ty)
|
|
{
|
|
return ty == IR::StringType || ty == IR::UrlType || ty == IR::ColorType;
|
|
}
|
|
|
|
IR::Type maxType(IR::Type left, IR::Type right)
|
|
{
|
|
if (isStringType(left) && isStringType(right)) {
|
|
// String promotions (url to string) are more specific than
|
|
// identity conversions (AKA left == right). That's because
|
|
// we want to ensure we convert urls to strings in binary
|
|
// expressions.
|
|
return IR::StringType;
|
|
} else if (left == right)
|
|
return left;
|
|
else if (isNumberType(left) && isNumberType(right)) {
|
|
IR::Type ty = qMax(left, right);
|
|
return ty == FloatType ? NumberType : ty; // promote floats
|
|
} else if ((isNumberType(left) && isStringType(right)) ||
|
|
(isNumberType(right) && isStringType(left)))
|
|
return IR::StringType;
|
|
else
|
|
return IR::InvalidType;
|
|
}
|
|
|
|
bool isRealType(IR::Type type)
|
|
{
|
|
return type == IR::NumberType || type == IR::FloatType;
|
|
}
|
|
|
|
const char *opname(AluOp op)
|
|
{
|
|
switch (op) {
|
|
case OpInvalid: return "?";
|
|
|
|
case OpIfTrue: return "(bool)";
|
|
case OpNot: return "!";
|
|
case OpUMinus: return "-";
|
|
case OpUPlus: return "+";
|
|
case OpCompl: return "~";
|
|
|
|
case OpBitAnd: return "&";
|
|
case OpBitOr: return "|";
|
|
case OpBitXor: return "^";
|
|
|
|
case OpAdd: return "+";
|
|
case OpSub: return "-";
|
|
case OpMul: return "*";
|
|
case OpDiv: return "/";
|
|
case OpMod: return "%";
|
|
|
|
case OpLShift: return "<<";
|
|
case OpRShift: return ">>";
|
|
case OpURShift: return ">>>";
|
|
|
|
case OpGt: return ">";
|
|
case OpLt: return "<";
|
|
case OpGe: return ">=";
|
|
case OpLe: return "<=";
|
|
case OpEqual: return "==";
|
|
case OpNotEqual: return "!=";
|
|
case OpStrictEqual: return "===";
|
|
case OpStrictNotEqual: return "!==";
|
|
|
|
case OpAnd: return "&&";
|
|
case OpOr: return "||";
|
|
|
|
default: return "?";
|
|
|
|
} // switch
|
|
}
|
|
|
|
AluOp binaryOperator(int op)
|
|
{
|
|
switch (static_cast<QSOperator::Op>(op)) {
|
|
case QSOperator::Add: return OpAdd;
|
|
case QSOperator::And: return OpAnd;
|
|
case QSOperator::BitAnd: return OpBitAnd;
|
|
case QSOperator::BitOr: return OpBitOr;
|
|
case QSOperator::BitXor: return OpBitXor;
|
|
case QSOperator::Div: return OpDiv;
|
|
case QSOperator::Equal: return OpEqual;
|
|
case QSOperator::Ge: return OpGe;
|
|
case QSOperator::Gt: return OpGt;
|
|
case QSOperator::Le: return OpLe;
|
|
case QSOperator::LShift: return OpLShift;
|
|
case QSOperator::Lt: return OpLt;
|
|
case QSOperator::Mod: return OpMod;
|
|
case QSOperator::Mul: return OpMul;
|
|
case QSOperator::NotEqual: return OpNotEqual;
|
|
case QSOperator::Or: return OpOr;
|
|
case QSOperator::RShift: return OpRShift;
|
|
case QSOperator::StrictEqual: return OpStrictEqual;
|
|
case QSOperator::StrictNotEqual: return OpStrictNotEqual;
|
|
case QSOperator::Sub: return OpSub;
|
|
case QSOperator::URShift: return OpURShift;
|
|
default: return OpInvalid;
|
|
}
|
|
}
|
|
|
|
void Const::dump(QTextStream &out)
|
|
{
|
|
switch (type) {
|
|
case QQmlJS::IR::UndefinedType:
|
|
out << "undefined";
|
|
break;
|
|
case QQmlJS::IR::NullType:
|
|
out << "null";
|
|
break;
|
|
case QQmlJS::IR::VoidType:
|
|
out << "void";
|
|
break;
|
|
case QQmlJS::IR::BoolType:
|
|
out << (value ? "true" : "false");
|
|
break;
|
|
default:
|
|
out << QString::number(value, 'g', 16);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void String::dump(QTextStream &out)
|
|
{
|
|
out << '"' << escape(*value) << '"';
|
|
}
|
|
|
|
QString String::escape(const QString &s)
|
|
{
|
|
QString r;
|
|
for (int i = 0; i < s.length(); ++i) {
|
|
const QChar ch = s.at(i);
|
|
if (ch == QLatin1Char('\n'))
|
|
r += QLatin1String("\\n");
|
|
else if (ch == QLatin1Char('\r'))
|
|
r += QLatin1String("\\r");
|
|
else if (ch == QLatin1Char('\\'))
|
|
r += QLatin1String("\\\\");
|
|
else if (ch == QLatin1Char('"'))
|
|
r += QLatin1String("\\\"");
|
|
else if (ch == QLatin1Char('\''))
|
|
r += QLatin1String("\\'");
|
|
else
|
|
r += ch;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void Name::init(Type type, const QString *id, quint32 line, quint32 column)
|
|
{
|
|
this->type = type;
|
|
this->id = id;
|
|
this->line = line;
|
|
this->column = column;
|
|
}
|
|
|
|
void Name::dump(QTextStream &out)
|
|
{
|
|
out << *id;
|
|
}
|
|
|
|
void Temp::dump(QTextStream &out)
|
|
{
|
|
out << '%' << index;
|
|
}
|
|
|
|
void Closure::dump(QTextStream &out)
|
|
{
|
|
QString name = value->name ? *value->name : QString();
|
|
if (name.isEmpty())
|
|
name.sprintf("%p", value);
|
|
out << "closure(" << name << ')';
|
|
}
|
|
|
|
void Unop::dump(QTextStream &out)
|
|
{
|
|
out << opname(op);
|
|
expr->dump(out);
|
|
}
|
|
|
|
Type Unop::typeForOp(AluOp op, Expr *expr)
|
|
{
|
|
switch (op) {
|
|
case OpIfTrue: return BoolType;
|
|
case OpNot: return BoolType;
|
|
|
|
case OpUMinus:
|
|
case OpUPlus:
|
|
case OpCompl:
|
|
return maxType(expr->type, NumberType);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return InvalidType;
|
|
}
|
|
|
|
void Binop::dump(QTextStream &out)
|
|
{
|
|
left->dump(out);
|
|
out << ' ' << opname(op) << ' ';
|
|
right->dump(out);
|
|
}
|
|
|
|
Type Binop::typeForOp(AluOp op, Expr *left, Expr *right)
|
|
{
|
|
if (! (left && right))
|
|
return InvalidType;
|
|
|
|
switch (op) {
|
|
case OpInvalid:
|
|
return InvalidType;
|
|
|
|
// unary operators
|
|
case OpIfTrue:
|
|
case OpNot:
|
|
case OpUMinus:
|
|
case OpUPlus:
|
|
case OpCompl:
|
|
return InvalidType;
|
|
|
|
// bit fields
|
|
case OpBitAnd:
|
|
case OpBitOr:
|
|
case OpBitXor:
|
|
return IntType;
|
|
|
|
case OpAdd:
|
|
if (left->type == StringType)
|
|
return StringType;
|
|
return NumberType;
|
|
|
|
case OpSub:
|
|
case OpMul:
|
|
case OpDiv:
|
|
case OpMod:
|
|
return NumberType;
|
|
|
|
case OpLShift:
|
|
case OpRShift:
|
|
case OpURShift:
|
|
return IntType;
|
|
|
|
case OpAnd:
|
|
case OpOr:
|
|
return BoolType;
|
|
|
|
case OpGt:
|
|
case OpLt:
|
|
case OpGe:
|
|
case OpLe:
|
|
case OpEqual:
|
|
case OpNotEqual:
|
|
case OpStrictEqual:
|
|
case OpStrictNotEqual:
|
|
return BoolType;
|
|
} // switch
|
|
|
|
return InvalidType;
|
|
}
|
|
|
|
void Call::dump(QTextStream &out)
|
|
{
|
|
base->dump(out);
|
|
out << '(';
|
|
for (ExprList *it = args; it; it = it->next) {
|
|
if (it != args)
|
|
out << ", ";
|
|
it->expr->dump(out);
|
|
}
|
|
out << ')';
|
|
}
|
|
|
|
Type Call::typeForFunction(Expr *)
|
|
{
|
|
return InvalidType;
|
|
}
|
|
|
|
void New::dump(QTextStream &out)
|
|
{
|
|
out << "new ";
|
|
base->dump(out);
|
|
out << '(';
|
|
for (ExprList *it = args; it; it = it->next) {
|
|
if (it != args)
|
|
out << ", ";
|
|
it->expr->dump(out);
|
|
}
|
|
out << ')';
|
|
}
|
|
|
|
Type New::typeForFunction(Expr *)
|
|
{
|
|
return InvalidType;
|
|
}
|
|
|
|
void Subscript::dump(QTextStream &out)
|
|
{
|
|
base->dump(out);
|
|
out << '[';
|
|
index->dump(out);
|
|
out << ']';
|
|
}
|
|
|
|
void Member::dump(QTextStream &out)
|
|
{
|
|
base->dump(out);
|
|
out << '.' << *name;
|
|
}
|
|
|
|
void Exp::dump(QTextStream &out, Mode)
|
|
{
|
|
out << "(void) ";
|
|
expr->dump(out);
|
|
out << ';';
|
|
}
|
|
|
|
void Enter::dump(QTextStream &out, Mode)
|
|
{
|
|
out << "%enter(";
|
|
expr->dump(out);
|
|
out << ");";
|
|
}
|
|
|
|
void Leave::dump(QTextStream &out, Mode)
|
|
{
|
|
out << "%leave";
|
|
out << ';';
|
|
}
|
|
|
|
void Move::dump(QTextStream &out, Mode)
|
|
{
|
|
target->dump(out);
|
|
out << ' ';
|
|
if (op != OpInvalid)
|
|
out << opname(op);
|
|
out << "= ";
|
|
// if (source->type != target->type)
|
|
// out << typeName(source->type) << "_to_" << typeName(target->type) << '(';
|
|
source->dump(out);
|
|
// if (source->type != target->type)
|
|
// out << ')';
|
|
out << ';';
|
|
}
|
|
|
|
void Jump::dump(QTextStream &out, Mode mode)
|
|
{
|
|
Q_UNUSED(mode);
|
|
out << "goto " << 'L' << target << ';';
|
|
}
|
|
|
|
void CJump::dump(QTextStream &out, Mode mode)
|
|
{
|
|
Q_UNUSED(mode);
|
|
out << "if (";
|
|
cond->dump(out);
|
|
if (mode == HIR)
|
|
out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';';
|
|
else
|
|
out << ") goto " << 'L' << iftrue << ";";
|
|
}
|
|
|
|
void Ret::dump(QTextStream &out, Mode)
|
|
{
|
|
out << "return";
|
|
if (expr) {
|
|
out << ' ';
|
|
expr->dump(out);
|
|
}
|
|
out << ';';
|
|
}
|
|
|
|
Function *Module::newFunction(const QString &name)
|
|
{
|
|
Function *f = new Function(this, name);
|
|
functions.append(f);
|
|
return f;
|
|
}
|
|
|
|
Function::~Function()
|
|
{
|
|
qDeleteAll(basicBlocks);
|
|
}
|
|
|
|
const QString *Function::newString(const QString &text)
|
|
{
|
|
return &*strings.insert(text);
|
|
}
|
|
|
|
BasicBlock *Function::newBasicBlock()
|
|
{
|
|
return i(new BasicBlock(this));
|
|
}
|
|
|
|
void Function::dump(QTextStream &out, Stmt::Mode mode)
|
|
{
|
|
QString n = name ? *name : QString();
|
|
if (n.isEmpty())
|
|
n.sprintf("%p", this);
|
|
out << "function " << n << "() {" << endl;
|
|
foreach (const QString *formal, formals)
|
|
out << "\treceive " << *formal << ';' << endl;
|
|
foreach (const QString *local, locals)
|
|
out << "\tlocal " << *local << ';' << endl;
|
|
foreach (BasicBlock *bb, basicBlocks)
|
|
bb->dump(out, mode);
|
|
out << '}' << endl;
|
|
}
|
|
|
|
unsigned BasicBlock::newTemp()
|
|
{
|
|
return function->tempCount++;
|
|
}
|
|
|
|
Temp *BasicBlock::TEMP(unsigned index)
|
|
{
|
|
Temp *e = function->New<Temp>();
|
|
e->init(IR::InvalidType, index);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::CONST(Type type, double value)
|
|
{
|
|
Const *e = function->New<Const>();
|
|
e->init(type, value);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::STRING(const QString *value)
|
|
{
|
|
String *e = function->New<String>();
|
|
e->init(value);
|
|
return e;
|
|
}
|
|
|
|
Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
|
|
{
|
|
Name *e = function->New<Name>();
|
|
e->init(InvalidType, function->newString(id), line, column);
|
|
return e;
|
|
}
|
|
|
|
Closure *BasicBlock::CLOSURE(Function *function)
|
|
{
|
|
Closure *clos = function->New<Closure>();
|
|
clos->init(IR::InvalidType, function);
|
|
return clos;
|
|
}
|
|
|
|
Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
|
|
{
|
|
Unop *e = function->New<Unop>();
|
|
e->init(op, expr);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
|
|
{
|
|
if (left && right) {
|
|
if (Const *c1 = left->asConst()) {
|
|
if (Const *c2 = right->asConst()) {
|
|
const IR::Type ty = Binop::typeForOp(op, left, right);
|
|
|
|
switch (op) {
|
|
case OpAdd: return CONST(ty, c1->value + c2->value);
|
|
case OpAnd: return CONST(ty, c1->value ? c2->value : 0);
|
|
case OpBitAnd: return CONST(ty, int(c1->value) & int(c2->value));
|
|
case OpBitOr: return CONST(ty, int(c1->value) | int(c2->value));
|
|
case OpBitXor: return CONST(ty, int(c1->value) ^ int(c2->value));
|
|
case OpDiv: return CONST(ty, c1->value / c2->value);
|
|
case OpEqual: return CONST(ty, c1->value == c2->value);
|
|
case OpGe: return CONST(ty, c1->value >= c2->value);
|
|
case OpGt: return CONST(ty, c1->value > c2->value);
|
|
case OpLe: return CONST(ty, c1->value <= c2->value);
|
|
case OpLShift: return CONST(ty, int(c1->value) << int(c2->value));
|
|
case OpLt: return CONST(ty, c1->value < c2->value);
|
|
case OpMod: return CONST(ty, ::fmod(c1->value, c2->value));
|
|
case OpMul: return CONST(ty, c1->value * c2->value);
|
|
case OpNotEqual: return CONST(ty, c1->value != c2->value);
|
|
case OpOr: return CONST(ty, c1->value ? c1->value : c2->value);
|
|
case OpRShift: return CONST(ty, int(c1->value) >> int(c2->value));
|
|
case OpStrictEqual: return CONST(ty, c1->value == c2->value);
|
|
case OpStrictNotEqual: return CONST(ty, c1->value != c2->value);
|
|
case OpSub: return CONST(ty, c1->value - c2->value);
|
|
case OpURShift: return CONST(ty, unsigned(c1->value) >> int(c2->value));
|
|
|
|
case OpIfTrue: // unary ops
|
|
case OpNot:
|
|
case OpUMinus:
|
|
case OpUPlus:
|
|
case OpCompl:
|
|
case OpInvalid:
|
|
break;
|
|
}
|
|
}
|
|
} else if (op == OpAdd) {
|
|
if (String *s1 = left->asString()) {
|
|
if (String *s2 = right->asString()) {
|
|
return STRING(function->newString(*s1->value + *s2->value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Binop *e = function->New<Binop>();
|
|
e->init(op, left, right);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::CALL(Expr *base, ExprList *args)
|
|
{
|
|
Call *e = function->New<Call>();
|
|
e->init(base, args);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::NEW(Expr *base, ExprList *args)
|
|
{
|
|
New *e = function->New<New>();
|
|
e->init(base, args);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index)
|
|
{
|
|
Subscript *e = function->New<Subscript>();
|
|
e->init(base, index);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::MEMBER(Expr *base, const QString *name)
|
|
{
|
|
Member*e = function->New<Member>();
|
|
e->init(base, name);
|
|
return e;
|
|
}
|
|
|
|
Stmt *BasicBlock::EXP(Expr *expr)
|
|
{
|
|
Exp *s = function->New<Exp>();
|
|
s->init(expr);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::ENTER(Expr *expr)
|
|
{
|
|
Enter *s = function->New<Enter>();
|
|
s->init(expr);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::LEAVE()
|
|
{
|
|
Leave *s = function->New<Leave>();
|
|
s->init();
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op)
|
|
{
|
|
Move *s = function->New<Move>();
|
|
s->init(target, source, op);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::JUMP(BasicBlock *target)
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
Jump *s = function->New<Jump>();
|
|
s->init(target);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
CJump *s = function->New<CJump>();
|
|
s->init(cond, iftrue, iffalse);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::RET(Expr *expr, Type type)
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
Ret *s = function->New<Ret>();
|
|
s->init(expr, type);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
void BasicBlock::dump(QTextStream &out, Stmt::Mode mode)
|
|
{
|
|
out << 'L' << this << ':' << endl;
|
|
foreach (Stmt *s, statements) {
|
|
out << '\t';
|
|
s->dump(out, mode);
|
|
out << endl;
|
|
}
|
|
}
|
|
|
|
} // end of namespace IR
|
|
} // end of namespace QQmlJS
|
|
|
|
QT_END_NAMESPACE
|