Fix rewrite of multiline string literals.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativerewrite.cpp
index 6311e01..688e75a 100644 (file)
@@ -1,34 +1,34 @@
 /****************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
 **
 ** This file is part of the QtDeclarative 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 Technology Preview License Agreement accompanying
-** this package.
-**
 ** 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.
+** 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
+** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
+** 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.
 **
 **
 **
@@ -39,9 +39,9 @@
 **
 ****************************************************************************/
 
-#include "private/qdeclarativerewrite_p.h"
+#include "qdeclarativerewrite_p.h"
 
-#include "private/qdeclarativeglobal_p.h"
+#include "qdeclarativeglobal_p.h"
 
 #include <QtCore/qdebug.h>
 
@@ -51,10 +51,48 @@ DEFINE_BOOL_CONFIG_OPTION(rewriteDump, QML_REWRITE_DUMP);
 
 namespace QDeclarativeRewrite {
 
+static void rewriteStringLiteral(AST::StringLiteral *ast, const QString *code, int startPosition, TextWriter *writer)
+{
+    const unsigned position = ast->firstSourceLocation().begin() - startPosition + 1;
+    const unsigned length = ast->literalToken.length - 2;
+    const QStringRef spell = code->midRef(position, length);
+    const int end = spell.size();
+    int index = 0;
+
+    while (index < end) {
+        const QChar ch = spell.at(index++);
+
+        if (index < end && ch == QLatin1Char('\\')) {
+            int pos = index;
+
+            // skip a possibly empty sequence of \r characters
+            while (pos < end && spell.at(pos) == QLatin1Char('\r'))
+                ++pos;
+
+            if (pos < end && spell.at(pos) == QLatin1Char('\n')) {
+                // This is a `\' followed by a newline terminator.
+                // In this case there's nothing to replace. We keep the code
+                // as it is and we resume the searching.
+                index = pos + 1; // refresh the index
+            }
+        } else if (ch == QLatin1Char('\r') || ch == QLatin1Char('\n')) {
+            const QString sep = ch == QLatin1Char('\r') ? QLatin1String("\\r") : QLatin1String("\\n");
+            const int pos = index - 1;
+            QString s = sep;
+
+            while (index < end && spell.at(index) == ch) {
+                s += sep;
+                ++index;
+            }
+
+            writer->replace(position + pos, index - pos, s);
+        }
+    }
+}
+
 bool SharedBindingTester::isSharable(const QString &code)
 {
     Engine engine;
-    NodePool pool(QString(), &engine);
     Lexer lexer(&engine);
     Parser parser(&engine);
     lexer.setCode(code, 0);
@@ -75,7 +113,6 @@ bool SharedBindingTester::isSharable(AST::Node *node)
 QString RewriteBinding::operator()(const QString &code, bool *ok, bool *sharable)
 {
     Engine engine;
-    NodePool pool(QString(), &engine);
     Lexer lexer(&engine);
     Parser parser(&engine);
     lexer.setCode(code, 0);
@@ -112,13 +149,14 @@ QString RewriteBinding::operator()(QDeclarativeJS::AST::Node *node, const QStrin
     _writer = &w;
     _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin();
     _inLoop = 0;
+    _code = &code;
 
     accept(node);
 
     unsigned startOfStatement = 0;
     unsigned endOfStatement = (expression ? expression->lastSourceLocation().end() : statement->lastSourceLocation().end()) - _position;
 
-    QString startString = QLatin1String("(function ") + QString::fromUtf8(_name) + QLatin1String("() { ");
+    QString startString = QLatin1String("(function ") + _name + QLatin1String("() { ");
     if (expression)
         startString += QLatin1String("return ");
     _writer->replace(startOfStatement, 0, startString);
@@ -135,7 +173,7 @@ QString RewriteBinding::operator()(QDeclarativeJS::AST::Node *node, const QStrin
 
     if (rewriteDump()) {
         qWarning() << "To:";
-        qWarning() << qPrintable(code);
+        qWarning() << qPrintable(codeCopy);
         qWarning() << "=============================================================";
     }
 
@@ -154,13 +192,14 @@ QString RewriteBinding::rewrite(QString code, unsigned position,
     _writer = &w;
     _position = position;
     _inLoop = 0;
+    _code = &code;
 
     accept(node);
 
     unsigned startOfStatement = node->firstSourceLocation().begin() - _position;
     unsigned endOfStatement = node->lastSourceLocation().end() - _position;
 
-    _writer->replace(startOfStatement, 0, QLatin1String("(function ") + QString::fromUtf8(_name) + QLatin1String("() { "));
+    _writer->replace(startOfStatement, 0, QLatin1String("(function ") + _name + QLatin1String("() { "));
     _writer->replace(endOfStatement, 0, QLatin1String(" })"));
 
     if (rewriteDump()) {
@@ -202,6 +241,12 @@ bool RewriteBinding::visit(AST::ExpressionStatement *ast)
     return false;
 }
 
+bool RewriteBinding::visit(AST::StringLiteral *ast)
+{
+    rewriteStringLiteral(ast, _code, _position, _writer);
+    return false;
+}
+
 bool RewriteBinding::visit(AST::DoWhileStatement *)
 {
     ++_inLoop;
@@ -268,6 +313,112 @@ void RewriteBinding::endVisit(AST::LocalForEachStatement *)
     --_inLoop;
 }
 
+bool RewriteBinding::visit(AST::CaseBlock *ast)
+{
+    // Process the initial sequence of the case clauses.
+    for (AST::CaseClauses *it = ast->clauses; it; it = it->next) {
+        // Return the value of the last statement in the block, if this is the last `case clause'
+        // of the switch statement.
+        bool returnTheValueOfLastStatement = (it->next == 0) && (ast->defaultClause == 0) && (ast->moreClauses == 0);
+
+        if (AST::CaseClause *clause = it->clause) {
+            accept(clause->expression);
+            rewriteCaseStatements(clause->statements, returnTheValueOfLastStatement);
+        }
+    }
+
+    // Process the default case clause
+    if (ast->defaultClause) {
+        // Return the value of the last statement in the block, if this is the last `case clause'
+        // of the switch statement.
+        bool rewriteTheLastStatement = (ast->moreClauses == 0);
+
+        rewriteCaseStatements(ast->defaultClause->statements, rewriteTheLastStatement);
+    }
+
+    // Process trailing `case clauses'
+    for (AST::CaseClauses *it = ast->moreClauses; it; it = it->next) {
+        // Return the value of the last statement in the block, if this is the last `case clause'
+        // of the switch statement.
+        bool returnTheValueOfLastStatement = (it->next == 0);
+
+        if (AST::CaseClause *clause = it->clause) {
+            accept(clause->expression);
+            rewriteCaseStatements(clause->statements, returnTheValueOfLastStatement);
+        }
+    }
+
+    return false;
+}
+
+void RewriteBinding::rewriteCaseStatements(AST::StatementList *statements, bool rewriteTheLastStatement)
+{
+    for (AST::StatementList *it = statements; it; it = it->next) {
+        if (it->next && AST::cast<AST::BreakStatement *>(it->next->statement) != 0) {
+            // The value of the first statement followed by a `break'.
+            accept(it->statement);
+            break;
+        } else if (!it->next) {
+            if (rewriteTheLastStatement)
+                accept(it->statement);
+            else if (AST::Block *block = AST::cast<AST::Block *>(it->statement))
+                rewriteCaseStatements(block->statements, rewriteTheLastStatement);
+        }
+    }
+}
+
+RewriteSignalHandler::RewriteSignalHandler()
+    : _writer(0)
+    , _code(0)
+    , _position(0)
+{
+}
+
+void RewriteSignalHandler::accept(AST::Node *node)
+{
+    AST::Node::acceptChild(node, this);
+}
+
+bool RewriteSignalHandler::visit(AST::StringLiteral *ast)
+{
+    rewriteStringLiteral(ast, _code, _position, _writer);
+    return false;
+}
+
+QString RewriteSignalHandler::operator()(QDeclarativeJS::AST::Node *node, const QString &code, const QString &name)
+{
+    if (rewriteDump()) {
+        qWarning() << "=============================================================";
+        qWarning() << "Rewrote:";
+        qWarning() << qPrintable(code);
+    }
+
+    QDeclarativeJS::AST::ExpressionNode *expression = node->expressionCast();
+    QDeclarativeJS::AST::Statement *statement = node->statementCast();
+    if (!expression && !statement)
+        return code;
+
+    TextWriter w;
+    _writer = &w;
+    _code = &code;
+
+    _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin();
+    accept(node);
+
+    QString rewritten = code;
+    w.write(&rewritten);
+
+    rewritten = QStringLiteral("(function ") + name + QStringLiteral("() { ") + rewritten + QStringLiteral(" })");
+
+    if (rewriteDump()) {
+        qWarning() << "To:";
+        qWarning() << qPrintable(rewritten);
+        qWarning() << "=============================================================";
+    }
+
+    return rewritten;
+}
+
 } // namespace QDeclarativeRewrite
 
 QT_END_NAMESPACE