/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the QtScript module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** 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, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, 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. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qscriptcompiler_p.h" #ifndef QT_NO_SCRIPT #include "qscriptast_p.h" #include "qscriptengine_p.h" #include "qscriptvalueimpl_p.h" #include "qscriptcontext_p.h" #include "qscriptmember_p.h" #include "qscriptobject_p.h" #include QT_BEGIN_NAMESPACE namespace QScript { class Compare : protected AST::Visitor { public: bool operator()(AST::ExpressionNode *e1, AST::ExpressionNode *e2) { if (!e1 || !e2) return (e1 == e2); if (e1->kind != e2->kind) return false; m_e2 = e2; m_equal = false; e1->accept(this); return m_equal; } protected: virtual bool visit(AST::ThisExpression *) { m_equal = true; return false; } virtual bool visit(AST::NullExpression *) { m_equal = true; return false; } virtual bool visit(AST::VoidExpression *) { m_equal = true; return false; } virtual bool visit(AST::FalseLiteral *) { m_equal = true; return false; } virtual bool visit(AST::TrueLiteral *) { m_equal = true; return false; } virtual bool visit(AST::NumericLiteral *e1) { AST::NumericLiteral *e2 = static_cast(m_e2); m_equal = (e1->value == e2->value); return false; } virtual bool visit(AST::RegExpLiteral *e1) { AST::RegExpLiteral *e2 = static_cast(m_e2); m_equal = (e1->pattern == e2->pattern) && (e1->flags == e2->flags); return false; } virtual bool visit(AST::StringLiteral *e1) { AST::StringLiteral *e2 = static_cast(m_e2); m_equal = (e1->value == e2->value); return false; } virtual bool visit(AST::IdentifierExpression *e1) { AST::IdentifierExpression *e2 = static_cast(m_e2); m_equal = (e1->name == e2->name); return false; } virtual bool visit(AST::ArrayMemberExpression *e1) { AST::ArrayMemberExpression *e2 = static_cast(m_e2); m_equal = operator()(e1->base, e2->base) && operator()(e1->expression, e2->expression); return false; } virtual bool visit(AST::FieldMemberExpression *e1) { AST::FieldMemberExpression *e2 = static_cast(m_e2); m_equal = (e1->name == e2->name) && operator()(e1->base, e2->base); return false; } virtual bool visit(AST::BinaryExpression *e1) { AST::BinaryExpression *e2 = static_cast(m_e2); m_equal = (e1->op == e2->op) && operator()(e1->left, e2->left) && operator()(e1->right, e2->right); return false; } virtual bool visit(AST::ConditionalExpression *e1) { AST::ConditionalExpression *e2 = static_cast(m_e2); m_equal = operator()(e1->expression, e2->expression) && operator()(e1->ok, e2->ok) && operator()(e1->ko, e2->ko); return false; } virtual bool visit(AST::TypeOfExpression *e1) { AST::TypeOfExpression *e2 = static_cast(m_e2); m_equal = operator()(e1->expression, e2->expression); return false; } virtual bool visit(AST::UnaryPlusExpression *e1) { AST::UnaryPlusExpression *e2 = static_cast(m_e2); m_equal = operator()(e1->expression, e2->expression); return false; } virtual bool visit(AST::UnaryMinusExpression *e1) { AST::UnaryMinusExpression *e2 = static_cast(m_e2); m_equal = operator()(e1->expression, e2->expression); return false; } virtual bool visit(AST::TildeExpression *e1) { AST::TildeExpression *e2 = static_cast(m_e2); m_equal = operator()(e1->expression, e2->expression); return false; } virtual bool visit(AST::NotExpression *e1) { AST::NotExpression *e2 = static_cast(m_e2); m_equal = operator()(e1->expression, e2->expression); return false; } virtual bool visit(AST::Expression *) { return false; } virtual bool visit(AST::ArrayLiteral *) { return false; } virtual bool visit(AST::ObjectLiteral *) { return false; } virtual bool visit(AST::CallExpression *) { return false; } virtual bool visit(AST::DeleteExpression *) { return false; } virtual bool visit(AST::FunctionExpression *) { return false; } virtual bool visit(AST::NewExpression *) { return false; } virtual bool visit(AST::NewMemberExpression *) { return false; } virtual bool visit(AST::PostDecrementExpression *) { return false; } virtual bool visit(AST::PostIncrementExpression *) { return false; } virtual bool visit(AST::PreDecrementExpression *) { return false; } virtual bool visit(AST::PreIncrementExpression *) { return false; } private: AST::ExpressionNode *m_e2; bool m_equal; }; class FetchName: protected AST::Visitor { public: inline FetchName(QScriptEnginePrivate *e): eng(e), name(0) {} QScriptNameIdImpl *operator() (AST::PropertyName *node) { name = 0; node->accept(this); return name; } protected: virtual bool visit(AST::IdentifierPropertyName *node) { name = node->id; return false; } virtual bool visit(AST::StringLiteralPropertyName *node) { name = node->id; return false; } virtual bool visit(AST::NumericLiteralPropertyName *node) { name = eng->nameId(QString::number(node->id), /*persistent=*/false); // ### don't use QString::number name->persistent = true; // ### remove return false; } private: QScriptEnginePrivate *eng; QScriptNameIdImpl *name; }; class EmptySourceElements: protected AST::Visitor { public: EmptySourceElements(QScriptEngine *d): driver(d), empty(false) {} inline bool operator () (AST::Node *) { empty = false; return empty; } private: QScriptEngine *driver; bool empty; }; class DeclareLocals: protected AST::Visitor { public: DeclareLocals(Compiler *c): compiler(c), eng(c->engine()) { } void operator () (AST::Node *node) { if (node) node->accept(this); } protected: virtual bool visit(AST::FunctionDeclaration *node) { compiler->iDeclareLocal(node->name, /*readOnly=*/false); return false; } virtual bool visit(AST::FunctionExpression *) { return false; } virtual bool visit(AST::VariableDeclaration *node) { compiler->iDeclareLocal(node->name, node->readOnly); return false; } private: Compiler *compiler; QScriptEnginePrivate *eng; }; Compiler::Compiler(QScriptEnginePrivate *eng): m_eng(eng), m_generateReferences(0), m_iterationStatement(0), m_switchStatement(0), m_withStatement(0), m_generateLeaveWithOnBreak(0), m_generateFastArgumentLookup(0), m_parseStatements(0), m_pad(0), m_topLevelCompiler(false), m_activeLoop(0) { } Compiler::~Compiler() { } bool Compiler::topLevelCompiler() const { return m_topLevelCompiler; } void Compiler::setTopLevelCompiler(bool b) { m_topLevelCompiler = b; } CompilationUnit Compiler::compile(AST::Node *node, const QList &formals) { m_formals = formals; m_generateReferences = 0; m_iterationStatement = 0; m_switchStatement = 0; m_withStatement = 0; m_generateLeaveWithOnBreak = 0; m_generateFastArgumentLookup = 0; m_parseStatements = 0; m_pad = 0; m_instructions.clear(); m_exceptionHandlers.clear(); m_generateFastArgumentLookup = false; // ### !formals.isEmpty(); // ### disabled for now.. it's buggy :( m_compilationUnit = CompilationUnit(); if (node) node->accept(this); // add a terminator if (topLevelCompiler()) { iHalt(); } else if (m_instructions.isEmpty() || m_instructions.last().op != QScriptInstruction::OP_Ret) { iLoadUndefined(); iRet(); } m_compilationUnit.setInstructions(m_instructions); m_compilationUnit.setExceptionHandlers(m_exceptionHandlers); return m_compilationUnit; } bool Compiler::preVisit(AST::Node *) { return m_compilationUnit.isValid(); } bool Compiler::visit(AST::SourceElements *node) { DeclareLocals declareLocals(this); declareLocals(node); bool was = changeParseStatements(false); for (AST::SourceElements *it = node; it != 0; it = it->next) it->element->accept(this); changeParseStatements(true); for (AST::SourceElements *it = node; it != 0; it = it->next) it->element->accept(this); changeParseStatements(was); return false; } bool Compiler::visit(AST::StatementList *) { return true; } bool Compiler::visit(AST::FunctionSourceElement *) { return m_parseStatements == 0; } bool Compiler::visit(AST::StatementSourceElement *) { return m_parseStatements; } bool Compiler::visit(AST::ThisExpression *) { iLoadThis(); return false; } bool Compiler::visit(AST::NullExpression *) { iLoadNull(); return false; } bool Compiler::visit(AST::RegExpLiteral *node) { Q_ASSERT(node->pattern != 0); if (node->flags) iNewRegExp(node->pattern, node->flags); else iNewRegExp(node->pattern); return false; } bool Compiler::visit(AST::NumericLiteral *node) { iLoadNumber(node->value); return false; } bool Compiler::visit(AST::StringLiteral *node) { iNewString(node->value); return false; } bool Compiler::visit(AST::ObjectLiteral *node) { iNewObject(); FetchName fetchName(m_eng); bool was = generateReferences(false); for (AST::PropertyNameAndValueList *it = node->properties; it != 0; it = it->next) { iLine(it->value); iDuplicate(); QScriptNameIdImpl *name = fetchName(it->name); Q_ASSERT(name != 0); iLoadString(name); iMakeReference(); it->value->accept(this); iPutField(); } generateReferences(was); return false; } bool Compiler::visit(AST::IdentifierExpression *node) { Q_ASSERT(node->name != 0); if (node->name == m_eng->idTable()->id_arguments) iLazyArguments(); if (m_generateReferences) iResolve(node->name); else iFetch(node->name); return false; } bool Compiler::visit(AST::FunctionDeclaration *node) { iLoadActivation(); iLoadString(node->name); iMakeReference(); iNewClosure(node); iPutField(); return false; } bool Compiler::visit(AST::FunctionExpression *node) { iNewClosure(node); if (node->name) { iDuplicate(); iLoadActivation(); iSwap(); iLoadString(node->name); iSwap(); iMakeReference(); iSwap(); iPutField(); } return false; } bool Compiler::visit(AST::CallExpression *node) { bool was = generateReferences(true); node->base->accept(this); generateReferences(false); int argc = 0; for (AST::ArgumentList *it = node->arguments; it != 0; it = it->next) { it->expression->accept(this); ++argc; } generateReferences(was); iCall(argc); return false; } bool Compiler::visit(AST::NewExpression *node) { bool was = generateReferences(true); node->expression->accept(this); generateReferences(was); iNew(0); return false; } bool Compiler::visit(AST::NewMemberExpression *node) { bool was = generateReferences(true); node->base->accept(this); generateReferences(false); int argc = 0; for (AST::ArgumentList *it = node->arguments; it != 0; it = it->next) { it->expression->accept(this); ++argc; } generateReferences(was); iNew(argc); return false; } bool Compiler::visit(AST::FieldMemberExpression *node) { bool was = generateReferences(false); node->base->accept(this); generateReferences(was); iLoadString(node->name); if (! was) iFetchField(); else iMakeReference(); return false; } bool Compiler::visit(AST::ArrayMemberExpression *node) { bool was = generateReferences(false); node->base->accept(this); node->expression->accept(this); generateReferences(was); if (! was) iFetchField(); else iMakeReference(); return false; } bool Compiler::visit(AST::PostIncrementExpression *node) { bool was = generateReferences(true); node->base->accept(this); generateReferences(was); iPostIncr(); return false; } bool Compiler::visit(AST::PostDecrementExpression *node) { bool was = generateReferences(true); node->base->accept(this); generateReferences(was); iPostDecr(); return false; } bool Compiler::visit(AST::PreIncrementExpression *node) { bool was = generateReferences(true); node->expression->accept(this); generateReferences(was); iIncr(); return false; } bool Compiler::visit(AST::PreDecrementExpression *node) { bool was = generateReferences(true); node->expression->accept(this); generateReferences(was); iDecr(); return false; } void Compiler::endVisit(AST::NotExpression *) { iNot(); } void Compiler::endVisit(AST::TildeExpression *) { iBitNot(); } bool Compiler::visit(AST::ThrowStatement *node) { iLine(node); return true; } bool Compiler::visit(AST::TryStatement *node) { int start = nextInstructionOffset(); if (node->statement) node->statement->accept(this); int end = nextInstructionOffset(); if (node->catchExpression) { iBranch(0); // skip the catch if no exception ExceptionHandlerDescriptor ehd(start, end, nextInstructionOffset()); m_exceptionHandlers.append(ehd); iBeginCatch(node->catchExpression->name); node->catchExpression->statement->accept(this); iEndCatch(); patchInstruction(end, nextInstructionOffset() - end); } if (node->finallyExpression) { if (!node->catchExpression) { ExceptionHandlerDescriptor ehd(start, end, nextInstructionOffset()); m_exceptionHandlers.prepend(ehd); } node->finallyExpression->statement->accept(this); } return false; } void Compiler::endVisit(AST::ThrowStatement *node) { if (! node->expression) iLoadUndefined(); iThrow(); } void Compiler::endVisit(AST::VoidExpression *) { iPop(); iLoadUndefined(); } bool Compiler::visit(AST::TypeOfExpression *node) { bool was = generateReferences(true); node->expression->accept(this); generateReferences(was); iTypeOf(); return false; } bool Compiler::visit(AST::DeleteExpression *node) { bool was = generateReferences(true); node->expression->accept(this); generateReferences(was); iDelete(); return false; } bool Compiler::visit(AST::ReturnStatement *node) { if (topLevelCompiler()) { m_compilationUnit.setError(QString::fromUtf8("return outside function body"), node->startLine); return false; } iLine(node); return true; } void Compiler::endVisit(AST::ReturnStatement *node) { if (! node->expression) iLoadUndefined(); iRet(); } bool Compiler::visit(AST::VariableStatement *node) { AST::VariableDeclarationList *lst = node->declarations; while (lst) { if (lst->declaration->expression) { iLine(node); break; } lst = lst->next; } return true; } bool Compiler::visit(AST::VariableDeclaration *node) { if (node->expression != 0) { iResolve(node->name); node->expression->accept(this); iAssign(); iPop(); } return false; } bool Compiler::visit(AST::ConditionalExpression *node) { node->expression->accept(this); int cond = nextInstructionOffset(); iBranchFalse(0); node->ok->accept(this); if (! node->ko) { patchInstruction(cond, nextInstructionOffset() - cond); } else { int terminator = nextInstructionOffset(); iBranch(0); node->ko->accept(this); patchInstruction(cond, terminator + 1 - cond); patchInstruction(terminator, nextInstructionOffset() - terminator); } return false; } bool Compiler::visit(AST::IfStatement *node) { iLine(node); node->expression->accept(this); int cond = nextInstructionOffset(); iBranchFalse(0); node->ok->accept(this); if (! node->ko) { patchInstruction(cond, nextInstructionOffset() - cond); } else { int terminator = nextInstructionOffset(); iBranch(0); node->ko->accept(this); patchInstruction(cond, terminator + 1 - cond); patchInstruction(terminator, nextInstructionOffset() - terminator); } if (!m_instructions.isEmpty() && m_instructions.last().op == QScriptInstruction::OP_Ret) iNop(); return false; } bool Compiler::visit(AST::Block *node) { if (node->statements && m_loops.contains(node)) { Loop &loop = m_loops[node]; node->statements->accept(this); loop.breakLabel.offset = nextInstructionOffset(); foreach (int index, loop.breakLabel.uses) { patchInstruction(index, loop.breakLabel.offset - index); } return false; } return true; } bool Compiler::visit(AST::WhileStatement *node) { Loop *previousLoop = changeActiveLoop(&m_loops[node]); m_activeLoop->continueLabel.offset = nextInstructionOffset(); iLine(node); int again = nextInstructionOffset(); node->expression->accept(this); int cond = nextInstructionOffset(); iBranchFalse(0); bool was = iterationStatement(true); bool was2 = generateLeaveOnBreak(false); node->statement->accept(this); generateLeaveOnBreak(was2); iterationStatement(was); iBranch(again - nextInstructionOffset()); patchInstruction(cond, nextInstructionOffset() - cond); m_activeLoop->breakLabel.offset = nextInstructionOffset(); foreach (int index, m_activeLoop->breakLabel.uses) { patchInstruction(index, m_activeLoop->breakLabel.offset - index); } foreach (int index, m_activeLoop->continueLabel.uses) { patchInstruction(index, m_activeLoop->continueLabel.offset - index); } changeActiveLoop(previousLoop); m_loops.remove(node); return false; } bool Compiler::visit(AST::DoWhileStatement *node) { Loop *previousLoop = changeActiveLoop(&m_loops[node]); int again = nextInstructionOffset(); iLine(node); bool was = iterationStatement(true); node->statement->accept(this); iterationStatement(was); m_activeLoop->continueLabel.offset = nextInstructionOffset(); node->expression->accept(this); iBranchTrue(again - nextInstructionOffset()); m_activeLoop->breakLabel.offset = nextInstructionOffset(); foreach (int index, m_activeLoop->breakLabel.uses) { patchInstruction(index, m_activeLoop->breakLabel.offset - index); } foreach (int index, m_activeLoop->continueLabel.uses) { patchInstruction(index, m_activeLoop->continueLabel.offset - index); } changeActiveLoop(previousLoop); m_loops.remove(node); return false; } bool Compiler::visit(AST::ForEachStatement *node) { Loop *previousLoop = changeActiveLoop(&m_loops[node]); iLine(node); node->expression->accept(this); iNewEnumeration(); iDuplicate(); iToFirstElement(); int again = nextInstructionOffset(); m_activeLoop->continueLabel.offset = again; iDuplicate(); iHasNextElement(); int cond = nextInstructionOffset(); iBranchFalse(0); bool was = generateReferences(true); node->initialiser->accept(this); generateReferences(was); iNextElement(); iAssign(); iPop(); was = iterationStatement(true); node->statement->accept(this); iterationStatement(was); iBranch(again - nextInstructionOffset()); patchInstruction(cond, nextInstructionOffset() - cond); m_activeLoop->breakLabel.offset = nextInstructionOffset(); iPop(); // pop the Enumeration foreach (int index, m_activeLoop->breakLabel.uses) { patchInstruction(index, m_activeLoop->breakLabel.offset - index); } foreach (int index, m_activeLoop->continueLabel.uses) { patchInstruction(index, m_activeLoop->continueLabel.offset - index); } changeActiveLoop(previousLoop); m_loops.remove(node); return false; } bool Compiler::visit(AST::LocalForEachStatement *node) { Loop *previousLoop = changeActiveLoop(&m_loops[node]); iLine(node); node->declaration->accept(this); node->expression->accept(this); iNewEnumeration(); iDuplicate(); iToFirstElement(); int again = nextInstructionOffset(); m_activeLoop->continueLabel.offset = again; iDuplicate(); iHasNextElement(); int cond = nextInstructionOffset(); iBranchFalse(0); iResolve(node->declaration->name); iNextElement(); iAssign(); iPop(); bool was = iterationStatement(true); node->statement->accept(this); iterationStatement(was); iBranch(again - nextInstructionOffset()); patchInstruction(cond, nextInstructionOffset() - cond); m_activeLoop->breakLabel.offset = nextInstructionOffset(); iPop(); // pop the Enumeration foreach (int index, m_activeLoop->breakLabel.uses) { patchInstruction(index, m_activeLoop->breakLabel.offset - index); } foreach (int index, m_activeLoop->continueLabel.uses) { patchInstruction(index, m_activeLoop->continueLabel.offset - index); } changeActiveLoop(previousLoop); m_loops.remove(node); return false; } void Compiler::visitForInternal(AST::Statement *node, AST::ExpressionNode *condition, AST::Statement *statement, AST::ExpressionNode *expression) { Q_ASSERT(statement != 0); int again = nextInstructionOffset(); if (condition != 0) { // iLine(condition); condition->accept(this); } else { // iLine(node); iLoadNumber(1); } int cond = nextInstructionOffset(); iBranchFalse(0); Loop *previousLoop = changeActiveLoop(&m_loops[node]); bool was = iterationStatement(true); statement->accept(this); iterationStatement(was); m_activeLoop->continueLabel.offset = nextInstructionOffset(); if (expression != 0) { expression->accept(this); iPop(); } iBranch(again - nextInstructionOffset()); patchInstruction(cond, nextInstructionOffset() - cond); m_activeLoop->breakLabel.offset = nextInstructionOffset(); foreach (int index, m_activeLoop->breakLabel.uses) { patchInstruction(index, m_activeLoop->breakLabel.offset - index); } foreach (int index, m_activeLoop->continueLabel.uses) { patchInstruction(index, m_activeLoop->continueLabel.offset - index); } changeActiveLoop(previousLoop); m_loops.remove(node); } bool Compiler::visit(AST::ForStatement *node) { iLine(node); if (node->initialiser != 0) { node->initialiser->accept(this); iPop(); } visitForInternal(node, node->condition, node->statement, node->expression); return false; } bool Compiler::visit(AST::LocalForStatement *node) { iLine(node); if (node->declarations) node->declarations->accept(this); visitForInternal(node, node->condition, node->statement, node->expression); return false; } bool Compiler::isAssignmentOperator(int op) const { switch (op) { case QSOperator::Assign: case QSOperator::InplaceAnd: case QSOperator::InplaceSub: case QSOperator::InplaceDiv: case QSOperator::InplaceAdd: case QSOperator::InplaceLeftShift: case QSOperator::InplaceMod: case QSOperator::InplaceMul: case QSOperator::InplaceOr: case QSOperator::InplaceRightShift: case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: return true; default: break; } return false; } int Compiler::inplaceAssignmentOperator(int op) const { switch (op) { case QSOperator::BitAnd: return QSOperator::InplaceAnd; case QSOperator::Sub: return QSOperator::InplaceSub; case QSOperator::Div: return QSOperator::InplaceDiv; case QSOperator::Add: return QSOperator::InplaceAdd; case QSOperator::LShift: return QSOperator::InplaceLeftShift; case QSOperator::Mod: return QSOperator::InplaceMod; case QSOperator::Mul: return QSOperator::InplaceMul; case QSOperator::BitOr: return QSOperator::InplaceOr; case QSOperator::RShift: return QSOperator::InplaceRightShift; case QSOperator::URShift: return QSOperator::InplaceURightShift; case QSOperator::BitXor: return QSOperator::InplaceXor; default: break; } return(-1); } bool Compiler::visit(AST::Expression *node) { node->left->accept(this); iPop(); // ### or iSync? node->right->accept(this); return false; } bool Compiler::visit(AST::BinaryExpression *node) { if (isAssignmentOperator(node->op)) { bool was = generateReferences(true); node->left->accept(this); generateReferences(was); } else { node->left->accept(this); } int address = 0; if (node->op == QSOperator::Or || node->op == QSOperator::And) { iDuplicate(); address = nextInstructionOffset(); if (node->op == QSOperator::Or) iBranchTrue(0); else iBranchFalse(0); iPop(); } int op = node->op; Compare compare; if ((op == QSOperator::Assign) && node->right->binaryExpressionCast() && (inplaceAssignmentOperator(node->right->binaryExpressionCast()->op) != -1) && compare(node->left, node->right->binaryExpressionCast()->left)) { // node->left is equivalent to node->right->left, so we generate // x op= y rather than x = x op y op = inplaceAssignmentOperator(node->right->binaryExpressionCast()->op); node->right->binaryExpressionCast()->right->accept(this); } else { node->right->accept(this); } switch (op) { case QSOperator::Assign: iAssign(); break; case QSOperator::InplaceAnd: iInplaceAnd(); break; case QSOperator::InplaceSub: iInplaceSub(); break; case QSOperator::InplaceDiv: iInplaceDiv(); break; case QSOperator::InplaceAdd: iInplaceAdd(); break; case QSOperator::InplaceLeftShift: iInplaceLeftShift(); break; case QSOperator::InplaceMod: iInplaceMod(); break; case QSOperator::InplaceMul: iInplaceMul(); break; case QSOperator::InplaceOr: iInplaceOr(); break; case QSOperator::InplaceRightShift: iInplaceRightShift(); break; case QSOperator::InplaceURightShift: iInplaceURightShift(); break; case QSOperator::InplaceXor: iInplaceXor(); break; case QSOperator::BitAnd: iBitAnd(); break; case QSOperator::BitOr: iBitOr(); break; case QSOperator::BitXor: iBitXor(); break; case QSOperator::LShift: iLeftShift(); break; case QSOperator::Mod: iMod(); break; case QSOperator::RShift: iRightShift(); break; case QSOperator::URShift: iURightShift(); break; case QSOperator::InstanceOf: iInstanceOf(); break; case QSOperator::Add: iAdd(); break; case QSOperator::And: patchInstruction(address, nextInstructionOffset() - address); break; case QSOperator::Div: iDiv(); break; case QSOperator::Equal: iEqual(); break; case QSOperator::Ge: iGreatOrEqual(); break; case QSOperator::Gt: iGreatThan(); break; case QSOperator::Le: iLessOrEqual(); break; case QSOperator::Lt: iLessThan(); break; case QSOperator::Mul: iMul(); break; case QSOperator::NotEqual: iNotEqual(); break; case QSOperator::Or: patchInstruction(address, nextInstructionOffset() - address); break; case QSOperator::Sub: iSub(); break; case QSOperator::StrictEqual: iStrictEqual(); break; case QSOperator::StrictNotEqual: iStrictNotEqual(); break; case QSOperator::In: iIn(); break; } return false; } bool Compiler::visit(AST::TrueLiteral *) { iLoadTrue(); return false; } bool Compiler::visit(AST::FalseLiteral *) { iLoadFalse(); return false; } bool Compiler::visit(AST::SwitchStatement *node) { iLine(node); Loop *previousLoop = changeActiveLoop(&m_loops[node]); node->expression->accept(this); bool was = switchStatement(true); AST::CaseClauses *clauses; int skipIndex = -1; int fallthroughIndex = -1; // ### make a function for this for (clauses = node->block->clauses; clauses != 0; clauses = clauses->next) { AST::CaseClause *clause = clauses->clause; if (skipIndex != -1) patchInstruction(skipIndex, nextInstructionOffset() - skipIndex); iDuplicate(); // expression clause->expression->accept(this); iStrictEqual(); skipIndex = nextInstructionOffset(); iBranchFalse(0); // next case if (fallthroughIndex != -1) // previous case falls through to here patchInstruction(fallthroughIndex, nextInstructionOffset() - fallthroughIndex); int breaksBefore = m_activeLoop->breakLabel.uses.count(); if (clause->statements) clause->statements->accept(this); int breaksAfter = m_activeLoop->breakLabel.uses.count(); if (breaksAfter == breaksBefore) { // fallthrough fallthroughIndex = nextInstructionOffset(); iBranch(0); } else { // no fallthrough (break) fallthroughIndex = -1; } } if (fallthroughIndex != -1) { patchInstruction(fallthroughIndex, nextInstructionOffset() - fallthroughIndex); fallthroughIndex = -1; } int defaultIndex = -1; if (node->block->defaultClause) { int skipDefaultIndex = -1; if (!node->block->clauses && node->block->moreClauses) { skipDefaultIndex = nextInstructionOffset(); iBranch(0); } defaultIndex = nextInstructionOffset(); int breaksBefore = m_activeLoop->breakLabel.uses.count(); if (node->block->defaultClause->statements) node->block->defaultClause->statements->accept(this); int breaksAfter = m_activeLoop->breakLabel.uses.count(); if (breaksAfter == breaksBefore) { // fallthrough fallthroughIndex = nextInstructionOffset(); iBranch(0); } else { // no fallthrough (break) fallthroughIndex = -1; } if (skipDefaultIndex != -1) patchInstruction(skipDefaultIndex, nextInstructionOffset() - skipDefaultIndex); } for (clauses = node->block->moreClauses; clauses != 0; clauses = clauses->next) { AST::CaseClause *clause = clauses->clause; if (skipIndex != -1) patchInstruction(skipIndex, nextInstructionOffset() - skipIndex); iDuplicate(); // expression clause->expression->accept(this); iStrictEqual(); skipIndex = nextInstructionOffset(); iBranchFalse(0); // next case if (fallthroughIndex != -1) // previous case falls through to here patchInstruction(fallthroughIndex, nextInstructionOffset() - fallthroughIndex); int breaksBefore = m_activeLoop->breakLabel.uses.count(); if (clause->statements) clause->statements->accept(this); int breaksAfter = m_activeLoop->breakLabel.uses.count(); if (breaksAfter == breaksBefore) { // fallthrough fallthroughIndex = nextInstructionOffset(); iBranch(0); } else { // no fallthrough (break) fallthroughIndex = -1; } } if (skipIndex != -1) { patchInstruction(skipIndex, nextInstructionOffset() - skipIndex); if (defaultIndex != -1) iBranch(defaultIndex - nextInstructionOffset()); // goto default } if (fallthroughIndex != -1) patchInstruction(fallthroughIndex, nextInstructionOffset() - fallthroughIndex); // backpatch the breaks int term = nextInstructionOffset(); foreach (int index, m_activeLoop->breakLabel.uses) { patchInstruction(index, term - index); } iPop(); // expression if (previousLoop && !m_activeLoop->continueLabel.uses.isEmpty()) { // join the continues and add to outer loop iBranch(3); foreach (int index, m_activeLoop->continueLabel.uses) { patchInstruction(index, nextInstructionOffset() - index); } iPop(); iBranch(0); previousLoop->continueLabel.uses.append(nextInstructionOffset() - 1); } switchStatement(was); changeActiveLoop(previousLoop); m_loops.remove(node); return false; } bool Compiler::visit(AST::LabelledStatement *node) { Loop *loop = findLoop(node->label); if (loop != 0) { QString str = m_eng->toString(node->label); m_compilationUnit.setError(QString::fromUtf8("duplicate label `%1'").arg(str), node->startLine); return false; } loop = &m_loops[node->statement]; loop->name = node->label; node->statement->accept(this); if (m_loops.contains(node->statement)) { loop->breakLabel.offset = nextInstructionOffset(); foreach (int index, loop->breakLabel.uses) { patchInstruction(index, loop->breakLabel.offset - index); } m_loops.remove(node->statement); } return false; } bool Compiler::visit(AST::ExpressionStatement *node) { if (node->expression) iLine(node->expression); return true; } void Compiler::endVisit(AST::ExpressionStatement *) { if (topLevelCompiler()) iSync(); else iPop(); } void Compiler::endVisit(AST::UnaryPlusExpression *) { iUnaryPlus(); } void Compiler::endVisit(AST::UnaryMinusExpression *) { iUnaryMinus(); } bool Compiler::visit(AST::ContinueStatement *node) { iLine(node); return true; } void Compiler::endVisit(AST::ContinueStatement *node) { int offset = nextInstructionOffset(); iBranch(0); Loop *loop = findLoop(node->label); if (!loop || !m_iterationStatement) { m_compilationUnit.setError(QString::fromUtf8("label not found"), node->startLine); return; } loop->continueLabel.uses.append(offset); } bool Compiler::visit(AST::BreakStatement *node) { iLine(node); return true; } void Compiler::endVisit(AST::BreakStatement *node) { Loop *loop = findLoop(node->label); if (! loop) { m_compilationUnit.setError(QString::fromUtf8("label not found"), node->startLine); return; } if (m_generateLeaveWithOnBreak) iLeaveWith(); int offset = nextInstructionOffset(); iBranch(0); loop->breakLabel.uses.append(offset); } void Compiler::endVisit(AST::EmptyStatement *node) { iLine(node); } bool Compiler::visit(AST::DebuggerStatement *node) { iLine(node); iDebugger(); return false; } void Compiler::patchInstruction(int index, int offset) { QScriptInstruction &i = m_instructions[index]; switch (i.op) { case QScriptInstruction::OP_Branch: case QScriptInstruction::OP_BranchFalse: case QScriptInstruction::OP_BranchTrue: m_eng->newInteger(&i.operand[0], offset); break; default: Q_ASSERT_X(0, "Compiler::patchInstruction()", "expected a branch instruction"); break; } } bool Compiler::visit(AST::WithStatement *node) { iLine(node); node->expression->accept(this); iEnterWith(); bool was = withStatement(true); bool was2 = generateLeaveOnBreak(true); node->statement->accept(this); generateLeaveOnBreak(was2); withStatement(was); iLeaveWith(); return false; } bool Compiler::visit(AST::ArrayLiteral *node) { iNewArray(); int length = 0; for (AST::ElementList *it = node->elements; it != 0; it = it->next) { for (AST::Elision *eit = it->elision; eit != 0; eit = eit->next) { iDuplicate(); iLoadNumber(length); iMakeReference(); iLoadUndefined(); iAssign(); iPop(); ++length; } if (it->expression) { iDuplicate(); iLoadNumber(length); iMakeReference(); it->expression->accept(this); iAssign(); iPop(); ++length; } } for (AST::Elision *eit = node->elision; eit != 0; eit = eit->next) { iDuplicate(); iLoadNumber(length); iMakeReference(); iLoadUndefined(); iAssign(); iPop(); ++length; } return false; } void Compiler::iLoadUndefined() { pushInstruction(QScriptInstruction::OP_LoadUndefined); } void Compiler::iLoadThis() { pushInstruction(QScriptInstruction::OP_LoadThis); } void Compiler::iLoadActivation() { pushInstruction(QScriptInstruction::OP_LoadActivation); } void Compiler::iLoadNull() { pushInstruction(QScriptInstruction::OP_LoadNull); } void Compiler::iLoadNumber(double number) { QScriptValueImpl arg0(number); pushInstruction(QScriptInstruction::OP_LoadNumber, arg0); } void Compiler::iLoadString(QScriptNameIdImpl *id) { QScriptValueImpl arg0; id->persistent = true; m_eng->newNameId(&arg0, id); pushInstruction(QScriptInstruction::OP_LoadString, arg0); } void Compiler::iDuplicate() { pushInstruction(QScriptInstruction::OP_Duplicate); } void Compiler::iSwap() { pushInstruction(QScriptInstruction::OP_Swap); } void Compiler::iResolve(QScriptNameIdImpl *id) { QScriptValueImpl arg0; id->persistent = true; m_eng->newNameId(&arg0, id); pushInstruction(QScriptInstruction::OP_Resolve, arg0); } void Compiler::iPutField() { pushInstruction(QScriptInstruction::OP_PutField); } void Compiler::iCall(int argc) { QScriptValueImpl arg0; m_eng->newInteger(&arg0, argc); pushInstruction(QScriptInstruction::OP_Call, arg0); } void Compiler::iNew(int argc) { QScriptValueImpl arg0; m_eng->newInteger(&arg0, argc); pushInstruction(QScriptInstruction::OP_New, arg0); } void Compiler::iFetchField() { pushInstruction(QScriptInstruction::OP_FetchField); } void Compiler::iLazyArguments() { pushInstruction(QScriptInstruction::OP_LazyArguments); } void Compiler::iRet() { pushInstruction(QScriptInstruction::OP_Ret); } void Compiler::iDeclareLocal(QScriptNameIdImpl *id, bool readOnly) { QScriptValueImpl arg0; id->persistent = true; m_eng->newNameId(&arg0, id); QScriptValueImpl arg1; m_eng->newInteger(&arg1, readOnly); pushInstruction(QScriptInstruction::OP_DeclareLocal, arg0, arg1); } void Compiler::iAssign() { pushInstruction(QScriptInstruction::OP_Assign); } void Compiler::iBitAnd() { pushInstruction(QScriptInstruction::OP_BitAnd); } void Compiler::iBitOr() { pushInstruction(QScriptInstruction::OP_BitOr); } void Compiler::iBitXor() { pushInstruction(QScriptInstruction::OP_BitXor); } void Compiler::iLeftShift() { pushInstruction(QScriptInstruction::OP_LeftShift); } void Compiler::iMod() { pushInstruction(QScriptInstruction::OP_Mod); } void Compiler::iRightShift() { pushInstruction(QScriptInstruction::OP_RightShift); } void Compiler::iURightShift() { pushInstruction(QScriptInstruction::OP_URightShift); } void Compiler::iAdd() { pushInstruction(QScriptInstruction::OP_Add); } void Compiler::iDiv() { pushInstruction(QScriptInstruction::OP_Div); } void Compiler::iEqual() { pushInstruction(QScriptInstruction::OP_Equal); } void Compiler::iGreatOrEqual() { pushInstruction(QScriptInstruction::OP_GreatOrEqual); } void Compiler::iGreatThan() { pushInstruction(QScriptInstruction::OP_GreatThan); } void Compiler::iLessOrEqual() { pushInstruction(QScriptInstruction::OP_LessOrEqual); } void Compiler::iLessThan() { pushInstruction(QScriptInstruction::OP_LessThan); } void Compiler::iMul() { pushInstruction(QScriptInstruction::OP_Mul); } void Compiler::iNotEqual() { pushInstruction(QScriptInstruction::OP_NotEqual); } void Compiler::iSub() { pushInstruction(QScriptInstruction::OP_Sub); } void Compiler::iStrictEqual() { pushInstruction(QScriptInstruction::OP_StrictEqual); } void Compiler::iStrictNotEqual() { pushInstruction(QScriptInstruction::OP_StrictNotEqual); } void Compiler::iBranch(int index) { QScriptValueImpl arg0; m_eng->newInteger(&arg0, index); pushInstruction(QScriptInstruction::OP_Branch, arg0); } void Compiler::iBranchFalse(int index) { QScriptValueImpl arg0; m_eng->newInteger(&arg0, index); pushInstruction(QScriptInstruction::OP_BranchFalse, arg0); } void Compiler::iBranchTrue(int index) { QScriptValueImpl arg0; m_eng->newInteger(&arg0, index); pushInstruction(QScriptInstruction::OP_BranchTrue, arg0); } void Compiler::iNewClosure(AST::FunctionExpression *expr) { QScriptValueImpl arg0; m_eng->newPointer(&arg0, expr); pushInstruction(QScriptInstruction::OP_NewClosure, arg0); } void Compiler::iIncr() { pushInstruction(QScriptInstruction::OP_Incr); } void Compiler::iDecr() { pushInstruction(QScriptInstruction::OP_Decr); } void Compiler::iPop() { pushInstruction(QScriptInstruction::OP_Pop); } void Compiler::iFetch(QScriptNameIdImpl *id) { if (m_generateFastArgumentLookup) { int index = m_formals.indexOf(id); if (index != -1) { QScriptValueImpl arg0; m_eng->newInteger(&arg0, index); pushInstruction(QScriptInstruction::OP_Receive, arg0); return; } } QScriptValueImpl arg0; id->persistent = true; m_eng->newNameId(&arg0, id); pushInstruction(QScriptInstruction::OP_Fetch, arg0); } void Compiler::iLoadTrue() { pushInstruction(QScriptInstruction::OP_LoadTrue); } void Compiler::iLoadFalse() { pushInstruction(QScriptInstruction::OP_LoadFalse); } void Compiler::iUnaryMinus() { pushInstruction(QScriptInstruction::OP_UnaryMinus); } void Compiler::iUnaryPlus() { pushInstruction(QScriptInstruction::OP_UnaryPlus); } void Compiler::iPostIncr() { pushInstruction(QScriptInstruction::OP_PostIncr); } void Compiler::iPostDecr() { pushInstruction(QScriptInstruction::OP_PostDecr); } void Compiler::iNewArray() { pushInstruction(QScriptInstruction::OP_NewArray); } void Compiler::iNewObject() { pushInstruction(QScriptInstruction::OP_NewObject); } void Compiler::iTypeOf() { pushInstruction(QScriptInstruction::OP_TypeOf); } void Compiler::iDelete() { pushInstruction(QScriptInstruction::OP_Delete); } void Compiler::iInstanceOf() { pushInstruction(QScriptInstruction::OP_InstanceOf); } void Compiler::iInplaceAnd() { pushInstruction(QScriptInstruction::OP_InplaceAnd); } void Compiler::iInplaceSub() { pushInstruction(QScriptInstruction::OP_InplaceSub); } void Compiler::iInplaceDiv() { pushInstruction(QScriptInstruction::OP_InplaceDiv); } void Compiler::iInplaceAdd() { pushInstruction(QScriptInstruction::OP_InplaceAdd); } void Compiler::iInplaceLeftShift() { pushInstruction(QScriptInstruction::OP_InplaceLeftShift); } void Compiler::iInplaceMod() { pushInstruction(QScriptInstruction::OP_InplaceMod); } void Compiler::iInplaceMul() { pushInstruction(QScriptInstruction::OP_InplaceMul); } void Compiler::iInplaceOr() { pushInstruction(QScriptInstruction::OP_InplaceOr); } void Compiler::iInplaceRightShift() { pushInstruction(QScriptInstruction::OP_InplaceRightShift); } void Compiler::iInplaceURightShift() { pushInstruction(QScriptInstruction::OP_InplaceURightShift); } void Compiler::iInplaceXor() { pushInstruction(QScriptInstruction::OP_InplaceXor); } void Compiler::iThrow() { pushInstruction(QScriptInstruction::OP_Throw); } void Compiler::iLine(AST::Node *node) { if (! node) return; QScriptValueImpl arg0; m_eng->newInteger(&arg0, node->startLine); QScriptValueImpl arg1; m_eng->newInteger(&arg1, node->startColumn); pushInstruction(QScriptInstruction::OP_Line, arg0, arg1); } void Compiler::iBitNot() { pushInstruction(QScriptInstruction::OP_BitNot); } void Compiler::iNot() { pushInstruction(QScriptInstruction::OP_Not); } void Compiler::iNewRegExp(QScriptNameIdImpl *pattern) { QScriptValueImpl arg0; pattern->persistent = true; m_eng->newNameId(&arg0, pattern); pushInstruction(QScriptInstruction::OP_NewRegExp, arg0); } void Compiler::iNewRegExp(QScriptNameIdImpl *pattern, int flags) { QScriptValueImpl arg0; pattern->persistent = true; m_eng->newNameId(&arg0, pattern); QScriptValueImpl arg1; m_eng->newInteger(&arg1, flags); pushInstruction(QScriptInstruction::OP_NewRegExp, arg0, arg1); } void Compiler::iNewEnumeration() { pushInstruction(QScriptInstruction::OP_NewEnumeration); } void Compiler::iToFirstElement() { pushInstruction(QScriptInstruction::OP_ToFirstElement); } void Compiler::iHasNextElement() { pushInstruction(QScriptInstruction::OP_HasNextElement); } void Compiler::iNextElement() { pushInstruction(QScriptInstruction::OP_NextElement); } void Compiler::iEnterWith() { pushInstruction(QScriptInstruction::OP_EnterWith); } void Compiler::iLeaveWith() { pushInstruction(QScriptInstruction::OP_LeaveWith); } void Compiler::iBeginCatch(QScriptNameIdImpl *id) { QScriptValueImpl arg0; id->persistent = true; m_eng->newNameId(&arg0, id); pushInstruction(QScriptInstruction::OP_BeginCatch, arg0); } void Compiler::iEndCatch() { pushInstruction(QScriptInstruction::OP_EndCatch); } void Compiler::iSync() { pushInstruction(QScriptInstruction::OP_Sync); } void Compiler::iHalt() { pushInstruction(QScriptInstruction::OP_Halt); } void Compiler::iMakeReference() { pushInstruction(QScriptInstruction::OP_MakeReference); } void Compiler::iIn() { pushInstruction(QScriptInstruction::OP_In); } void Compiler::iNop() { pushInstruction(QScriptInstruction::OP_Nop); } void Compiler::iNewString(QScriptNameIdImpl *id) { QScriptValueImpl arg0; id->persistent = true; m_eng->newNameId(&arg0, id); pushInstruction(QScriptInstruction::OP_NewString, arg0); } void Compiler::iDebugger() { pushInstruction(QScriptInstruction::OP_Debugger); } Compiler::Loop *Compiler::findLoop(QScriptNameIdImpl *name) { if (! name) return m_activeLoop; QMap::iterator it = m_loops.begin(); for (; it != m_loops.end(); ++it) { Loop &loop = *it; if (loop.name == name) return &loop; } return 0; } } // namespace QScript QT_END_NAMESPACE #endif // QT_NO_SCRIPT