Fix rewrite of multiline string literals.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativerewrite.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativerewrite_p.h"
43
44 #include "qdeclarativeglobal_p.h"
45
46 #include <QtCore/qdebug.h>
47
48 QT_BEGIN_NAMESPACE
49
50 DEFINE_BOOL_CONFIG_OPTION(rewriteDump, QML_REWRITE_DUMP);
51
52 namespace QDeclarativeRewrite {
53
54 static void rewriteStringLiteral(AST::StringLiteral *ast, const QString *code, int startPosition, TextWriter *writer)
55 {
56     const unsigned position = ast->firstSourceLocation().begin() - startPosition + 1;
57     const unsigned length = ast->literalToken.length - 2;
58     const QStringRef spell = code->midRef(position, length);
59     const int end = spell.size();
60     int index = 0;
61
62     while (index < end) {
63         const QChar ch = spell.at(index++);
64
65         if (index < end && ch == QLatin1Char('\\')) {
66             int pos = index;
67
68             // skip a possibly empty sequence of \r characters
69             while (pos < end && spell.at(pos) == QLatin1Char('\r'))
70                 ++pos;
71
72             if (pos < end && spell.at(pos) == QLatin1Char('\n')) {
73                 // This is a `\' followed by a newline terminator.
74                 // In this case there's nothing to replace. We keep the code
75                 // as it is and we resume the searching.
76                 index = pos + 1; // refresh the index
77             }
78         } else if (ch == QLatin1Char('\r') || ch == QLatin1Char('\n')) {
79             const QString sep = ch == QLatin1Char('\r') ? QLatin1String("\\r") : QLatin1String("\\n");
80             const int pos = index - 1;
81             QString s = sep;
82
83             while (index < end && spell.at(index) == ch) {
84                 s += sep;
85                 ++index;
86             }
87
88             writer->replace(position + pos, index - pos, s);
89         }
90     }
91 }
92
93 bool SharedBindingTester::isSharable(const QString &code)
94 {
95     Engine engine;
96     Lexer lexer(&engine);
97     Parser parser(&engine);
98     lexer.setCode(code, 0);
99     parser.parseStatement();
100     if (!parser.statement()) 
101         return false;
102
103     return isSharable(parser.statement());
104 }
105
106 bool SharedBindingTester::isSharable(AST::Node *node)
107 {
108     _sharable = true;
109     AST::Node::acceptChild(node, this);
110     return _sharable;
111 }
112
113 QString RewriteBinding::operator()(const QString &code, bool *ok, bool *sharable)
114 {
115     Engine engine;
116     Lexer lexer(&engine);
117     Parser parser(&engine);
118     lexer.setCode(code, 0);
119     parser.parseStatement();
120     if (!parser.statement()) {
121         if (ok) *ok = false;
122         return QString();
123     } else {
124         if (ok) *ok = true;
125         if (sharable) {
126             SharedBindingTester tester;
127             *sharable = tester.isSharable(parser.statement());
128         }
129     }
130     return rewrite(code, 0, parser.statement());
131 }
132
133 QString RewriteBinding::operator()(QDeclarativeJS::AST::Node *node, const QString &code, bool *sharable)
134 {
135     if (!node)
136         return code;
137
138     if (sharable) {
139         SharedBindingTester tester;
140         *sharable = tester.isSharable(node);
141     }
142
143     QDeclarativeJS::AST::ExpressionNode *expression = node->expressionCast();
144     QDeclarativeJS::AST::Statement *statement = node->statementCast();
145     if(!expression && !statement)
146         return code;
147
148     TextWriter w;
149     _writer = &w;
150     _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin();
151     _inLoop = 0;
152     _code = &code;
153
154     accept(node);
155
156     unsigned startOfStatement = 0;
157     unsigned endOfStatement = (expression ? expression->lastSourceLocation().end() : statement->lastSourceLocation().end()) - _position;
158
159     QString startString = QLatin1String("(function ") + _name + QLatin1String("() { ");
160     if (expression)
161         startString += QLatin1String("return ");
162     _writer->replace(startOfStatement, 0, startString);
163     _writer->replace(endOfStatement, 0, QLatin1String(" })"));
164
165     if (rewriteDump()) {
166         qWarning() << "=============================================================";
167         qWarning() << "Rewrote:";
168         qWarning() << qPrintable(code);
169     }
170
171     QString codeCopy = code;
172     w.write(&codeCopy);
173
174     if (rewriteDump()) {
175         qWarning() << "To:";
176         qWarning() << qPrintable(codeCopy);
177         qWarning() << "=============================================================";
178     }
179
180     return codeCopy;
181 }
182
183 void RewriteBinding::accept(AST::Node *node)
184 {
185     AST::Node::acceptChild(node, this);
186 }
187
188 QString RewriteBinding::rewrite(QString code, unsigned position,
189                                 AST::Statement *node)
190 {
191     TextWriter w;
192     _writer = &w;
193     _position = position;
194     _inLoop = 0;
195     _code = &code;
196
197     accept(node);
198
199     unsigned startOfStatement = node->firstSourceLocation().begin() - _position;
200     unsigned endOfStatement = node->lastSourceLocation().end() - _position;
201
202     _writer->replace(startOfStatement, 0, QLatin1String("(function ") + _name + QLatin1String("() { "));
203     _writer->replace(endOfStatement, 0, QLatin1String(" })"));
204
205     if (rewriteDump()) {
206         qWarning() << "=============================================================";
207         qWarning() << "Rewrote:";
208         qWarning() << qPrintable(code);
209     }
210
211     w.write(&code);
212
213     if (rewriteDump()) {
214         qWarning() << "To:";
215         qWarning() << qPrintable(code);
216         qWarning() << "=============================================================";
217     }
218
219     return code;
220 }
221
222 bool RewriteBinding::visit(AST::Block *ast)
223 {
224     for (AST::StatementList *it = ast->statements; it; it = it->next) {
225         if (! it->next) {
226             // we need to rewrite only the last statement of a block.
227             accept(it->statement);
228         }
229     }
230
231     return false;
232 }
233
234 bool RewriteBinding::visit(AST::ExpressionStatement *ast)
235 {
236     if (! _inLoop) {
237         unsigned startOfExpressionStatement = ast->firstSourceLocation().begin() - _position;
238         _writer->replace(startOfExpressionStatement, 0, QLatin1String("return "));
239     }
240
241     return false;
242 }
243
244 bool RewriteBinding::visit(AST::StringLiteral *ast)
245 {
246     rewriteStringLiteral(ast, _code, _position, _writer);
247     return false;
248 }
249
250 bool RewriteBinding::visit(AST::DoWhileStatement *)
251 {
252     ++_inLoop;
253     return true;
254 }
255
256 void RewriteBinding::endVisit(AST::DoWhileStatement *)
257 {
258     --_inLoop;
259 }
260
261 bool RewriteBinding::visit(AST::WhileStatement *)
262 {
263     ++_inLoop;
264     return true;
265 }
266
267 void RewriteBinding::endVisit(AST::WhileStatement *)
268 {
269     --_inLoop;
270 }
271
272 bool RewriteBinding::visit(AST::ForStatement *)
273 {
274     ++_inLoop;
275     return true;
276 }
277
278 void RewriteBinding::endVisit(AST::ForStatement *)
279 {
280     --_inLoop;
281 }
282
283 bool RewriteBinding::visit(AST::LocalForStatement *)
284 {
285     ++_inLoop;
286     return true;
287 }
288
289 void RewriteBinding::endVisit(AST::LocalForStatement *)
290 {
291     --_inLoop;
292 }
293
294 bool RewriteBinding::visit(AST::ForEachStatement *)
295 {
296     ++_inLoop;
297     return true;
298 }
299
300 void RewriteBinding::endVisit(AST::ForEachStatement *)
301 {
302     --_inLoop;
303 }
304
305 bool RewriteBinding::visit(AST::LocalForEachStatement *)
306 {
307     ++_inLoop;
308     return true;
309 }
310
311 void RewriteBinding::endVisit(AST::LocalForEachStatement *)
312 {
313     --_inLoop;
314 }
315
316 bool RewriteBinding::visit(AST::CaseBlock *ast)
317 {
318     // Process the initial sequence of the case clauses.
319     for (AST::CaseClauses *it = ast->clauses; it; it = it->next) {
320         // Return the value of the last statement in the block, if this is the last `case clause'
321         // of the switch statement.
322         bool returnTheValueOfLastStatement = (it->next == 0) && (ast->defaultClause == 0) && (ast->moreClauses == 0);
323
324         if (AST::CaseClause *clause = it->clause) {
325             accept(clause->expression);
326             rewriteCaseStatements(clause->statements, returnTheValueOfLastStatement);
327         }
328     }
329
330     // Process the default case clause
331     if (ast->defaultClause) {
332         // Return the value of the last statement in the block, if this is the last `case clause'
333         // of the switch statement.
334         bool rewriteTheLastStatement = (ast->moreClauses == 0);
335
336         rewriteCaseStatements(ast->defaultClause->statements, rewriteTheLastStatement);
337     }
338
339     // Process trailing `case clauses'
340     for (AST::CaseClauses *it = ast->moreClauses; it; it = it->next) {
341         // Return the value of the last statement in the block, if this is the last `case clause'
342         // of the switch statement.
343         bool returnTheValueOfLastStatement = (it->next == 0);
344
345         if (AST::CaseClause *clause = it->clause) {
346             accept(clause->expression);
347             rewriteCaseStatements(clause->statements, returnTheValueOfLastStatement);
348         }
349     }
350
351     return false;
352 }
353
354 void RewriteBinding::rewriteCaseStatements(AST::StatementList *statements, bool rewriteTheLastStatement)
355 {
356     for (AST::StatementList *it = statements; it; it = it->next) {
357         if (it->next && AST::cast<AST::BreakStatement *>(it->next->statement) != 0) {
358             // The value of the first statement followed by a `break'.
359             accept(it->statement);
360             break;
361         } else if (!it->next) {
362             if (rewriteTheLastStatement)
363                 accept(it->statement);
364             else if (AST::Block *block = AST::cast<AST::Block *>(it->statement))
365                 rewriteCaseStatements(block->statements, rewriteTheLastStatement);
366         }
367     }
368 }
369
370 RewriteSignalHandler::RewriteSignalHandler()
371     : _writer(0)
372     , _code(0)
373     , _position(0)
374 {
375 }
376
377 void RewriteSignalHandler::accept(AST::Node *node)
378 {
379     AST::Node::acceptChild(node, this);
380 }
381
382 bool RewriteSignalHandler::visit(AST::StringLiteral *ast)
383 {
384     rewriteStringLiteral(ast, _code, _position, _writer);
385     return false;
386 }
387
388 QString RewriteSignalHandler::operator()(QDeclarativeJS::AST::Node *node, const QString &code, const QString &name)
389 {
390     if (rewriteDump()) {
391         qWarning() << "=============================================================";
392         qWarning() << "Rewrote:";
393         qWarning() << qPrintable(code);
394     }
395
396     QDeclarativeJS::AST::ExpressionNode *expression = node->expressionCast();
397     QDeclarativeJS::AST::Statement *statement = node->statementCast();
398     if (!expression && !statement)
399         return code;
400
401     TextWriter w;
402     _writer = &w;
403     _code = &code;
404
405     _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin();
406     accept(node);
407
408     QString rewritten = code;
409     w.write(&rewritten);
410
411     rewritten = QStringLiteral("(function ") + name + QStringLiteral("() { ") + rewritten + QStringLiteral(" })");
412
413     if (rewriteDump()) {
414         qWarning() << "To:";
415         qWarning() << qPrintable(rewritten);
416         qWarning() << "=============================================================";
417     }
418
419     return rewritten;
420 }
421
422 } // namespace QDeclarativeRewrite
423
424 QT_END_NAMESPACE