583 lines
13 KiB
C++
583 lines
13 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/legal
|
|
**
|
|
** This file is part of the V4VM module of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, 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, Digia gives you certain additional
|
|
** rights. These rights are described in the Digia 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.
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qv4ir_p.h"
|
|
#include <private/qqmljsast_p.h>
|
|
|
|
#include <QtCore/qtextstream.h>
|
|
#include <QtCore/qdebug.h>
|
|
#include <math.h>
|
|
#include <cassert>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
namespace QQmlJS {
|
|
namespace IR {
|
|
|
|
const char *typeName(Type t)
|
|
{
|
|
switch (t) {
|
|
case UndefinedType: return "undefined";
|
|
case NullType: return "null";
|
|
case BoolType: return "bool";
|
|
case NumberType: return "number";
|
|
default: return "invalid";
|
|
}
|
|
}
|
|
|
|
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 OpInstanceof: return "instanceof";
|
|
case OpIn: return "in";
|
|
|
|
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;
|
|
case QSOperator::InstanceOf: return OpInstanceof;
|
|
case QSOperator::In: return OpIn;
|
|
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::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 += QStringLiteral("\\n");
|
|
else if (ch == QLatin1Char('\r'))
|
|
r += QStringLiteral("\\r");
|
|
else if (ch == QLatin1Char('\\'))
|
|
r += QStringLiteral("\\\\");
|
|
else if (ch == QLatin1Char('"'))
|
|
r += QStringLiteral("\\\"");
|
|
else if (ch == QLatin1Char('\''))
|
|
r += QStringLiteral("\\'");
|
|
else
|
|
r += ch;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void Name::init(const QString *id, quint32 line, quint32 column)
|
|
{
|
|
this->id = id;
|
|
this->builtin = builtin_invalid;
|
|
this->line = line;
|
|
this->column = column;
|
|
}
|
|
|
|
void Name::init(Builtin builtin, quint32 line, quint32 column)
|
|
{
|
|
this->id = 0;
|
|
this->builtin = builtin;
|
|
this->line = line;
|
|
this->column = column;
|
|
}
|
|
|
|
void Name::dump(QTextStream &out)
|
|
{
|
|
if (id)
|
|
out << *id;
|
|
else
|
|
out << "__qmljs_builtin_%" << (int) builtin;
|
|
}
|
|
|
|
void Temp::dump(QTextStream &out)
|
|
{
|
|
if (index < 0) {
|
|
out << '#' << -(index + 1); // negative and 1-based.
|
|
} else {
|
|
out << '%' << index; // temp
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void Binop::dump(QTextStream &out)
|
|
{
|
|
left->dump(out);
|
|
out << ' ' << opname(op) << ' ';
|
|
right->dump(out);
|
|
}
|
|
|
|
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 << ')';
|
|
}
|
|
|
|
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 << ')';
|
|
}
|
|
|
|
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->index << ';';
|
|
}
|
|
|
|
void CJump::dump(QTextStream &out, Mode mode)
|
|
{
|
|
Q_UNUSED(mode);
|
|
out << "if (";
|
|
cond->dump(out);
|
|
if (mode == HIR)
|
|
out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';';
|
|
else
|
|
out << ") goto " << 'L' << iftrue->index << ";";
|
|
}
|
|
|
|
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(BasicBlockInsertMode mode)
|
|
{
|
|
BasicBlock *block = new BasicBlock(this);
|
|
return mode == InsertBlock ? insertBasicBlock(block) : block;
|
|
}
|
|
|
|
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(int index)
|
|
{
|
|
Temp *e = function->New<Temp>();
|
|
e->init(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(function->newString(id), line, column);
|
|
return e;
|
|
}
|
|
|
|
Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column)
|
|
{
|
|
Name *e = function->New<Name>();
|
|
e->init(builtin, line, column);
|
|
return e;
|
|
}
|
|
|
|
Closure *BasicBlock::CLOSURE(Function *function)
|
|
{
|
|
Closure *clos = function->New<Closure>();
|
|
clos->init(function);
|
|
return clos;
|
|
}
|
|
|
|
Expr *BasicBlock::UNOP(AluOp op, Temp *expr)
|
|
{
|
|
Unop *e = function->New<Unop>();
|
|
e->init(op, expr);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::BINOP(AluOp op, Temp *left, Temp *right)
|
|
{
|
|
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(Temp *base, Temp *index)
|
|
{
|
|
Subscript *e = function->New<Subscript>();
|
|
e->init(base, index);
|
|
return e;
|
|
}
|
|
|
|
Expr *BasicBlock::MEMBER(Temp *base, const QString *name)
|
|
{
|
|
Member*e = function->New<Member>();
|
|
e->init(base, name);
|
|
return e;
|
|
}
|
|
|
|
Stmt *BasicBlock::EXP(Expr *expr)
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
Exp *s = function->New<Exp>();
|
|
s->init(expr);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::ENTER(Expr *expr)
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
Enter *s = function->New<Enter>();
|
|
s->init(expr);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::LEAVE()
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
Leave *s = function->New<Leave>();
|
|
s->init();
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op)
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
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);
|
|
|
|
assert(! out.contains(target));
|
|
out.append(target);
|
|
|
|
assert(! target->in.contains(this));
|
|
target->in.append(this);
|
|
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
if (iftrue == iffalse) {
|
|
MOVE(TEMP(newTemp()), cond);
|
|
return JUMP(iftrue);
|
|
}
|
|
|
|
CJump *s = function->New<CJump>();
|
|
s->init(cond, iftrue, iffalse);
|
|
statements.append(s);
|
|
|
|
assert(! out.contains(iftrue));
|
|
out.append(iftrue);
|
|
|
|
assert(! iftrue->in.contains(this));
|
|
iftrue->in.append(this);
|
|
|
|
assert(! out.contains(iffalse));
|
|
out.append(iffalse);
|
|
|
|
assert(! iffalse->in.contains(this));
|
|
iffalse->in.append(this);
|
|
|
|
return s;
|
|
}
|
|
|
|
Stmt *BasicBlock::RET(Temp *expr)
|
|
{
|
|
if (isTerminated())
|
|
return 0;
|
|
|
|
Ret *s = function->New<Ret>();
|
|
s->init(expr);
|
|
statements.append(s);
|
|
return s;
|
|
}
|
|
|
|
void BasicBlock::dump(QTextStream &out, Stmt::Mode mode)
|
|
{
|
|
out << 'L' << index << ':' << endl;
|
|
foreach (Stmt *s, statements) {
|
|
out << '\t';
|
|
s->dump(out, mode);
|
|
out << endl;
|
|
}
|
|
}
|
|
|
|
} // end of namespace IR
|
|
} // end of namespace QQmlJS
|
|
|
|
QT_END_NAMESPACE
|