77da9437048efe3f02b8e44bec761d2d32490f89
[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 bool SharedBindingTester::isSharable(const QString &code)
55 {
56     Engine engine;
57     Lexer lexer(&engine);
58     Parser parser(&engine);
59     lexer.setCode(code, 0);
60     parser.parseStatement();
61     if (!parser.statement()) 
62         return false;
63
64     return isSharable(parser.statement());
65 }
66
67 bool SharedBindingTester::isSharable(AST::Node *node)
68 {
69     _sharable = true;
70     AST::Node::acceptChild(node, this);
71     return _sharable;
72 }
73
74 QString RewriteBinding::operator()(const QString &code, bool *ok, bool *sharable)
75 {
76     Engine engine;
77     Lexer lexer(&engine);
78     Parser parser(&engine);
79     lexer.setCode(code, 0);
80     parser.parseStatement();
81     if (!parser.statement()) {
82         if (ok) *ok = false;
83         return QString();
84     } else {
85         if (ok) *ok = true;
86         if (sharable) {
87             SharedBindingTester tester;
88             *sharable = tester.isSharable(parser.statement());
89         }
90     }
91     return rewrite(code, 0, parser.statement());
92 }
93
94 QString RewriteBinding::operator()(QDeclarativeJS::AST::Node *node, const QString &code, bool *sharable)
95 {
96     if (!node)
97         return code;
98
99     if (sharable) {
100         SharedBindingTester tester;
101         *sharable = tester.isSharable(node);
102     }
103
104     QDeclarativeJS::AST::ExpressionNode *expression = node->expressionCast();
105     QDeclarativeJS::AST::Statement *statement = node->statementCast();
106     if(!expression && !statement)
107         return code;
108
109     TextWriter w;
110     _writer = &w;
111     _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin();
112     _inLoop = 0;
113     _code = &code;
114
115     accept(node);
116
117     unsigned startOfStatement = 0;
118     unsigned endOfStatement = (expression ? expression->lastSourceLocation().end() : statement->lastSourceLocation().end()) - _position;
119
120     QString startString = QLatin1String("(function ") + _name + QLatin1String("() { ");
121     if (expression)
122         startString += QLatin1String("return ");
123     _writer->replace(startOfStatement, 0, startString);
124     _writer->replace(endOfStatement, 0, QLatin1String(" })"));
125
126     if (rewriteDump()) {
127         qWarning() << "=============================================================";
128         qWarning() << "Rewrote:";
129         qWarning() << qPrintable(code);
130     }
131
132     QString codeCopy = code;
133     w.write(&codeCopy);
134
135     if (rewriteDump()) {
136         qWarning() << "To:";
137         qWarning() << qPrintable(codeCopy);
138         qWarning() << "=============================================================";
139     }
140
141     return codeCopy;
142 }
143
144 void RewriteBinding::accept(AST::Node *node)
145 {
146     AST::Node::acceptChild(node, this);
147 }
148
149 QString RewriteBinding::rewrite(QString code, unsigned position,
150                                 AST::Statement *node)
151 {
152     TextWriter w;
153     _writer = &w;
154     _position = position;
155     _inLoop = 0;
156     _code = &code;
157
158     accept(node);
159
160     unsigned startOfStatement = node->firstSourceLocation().begin() - _position;
161     unsigned endOfStatement = node->lastSourceLocation().end() - _position;
162
163     _writer->replace(startOfStatement, 0, QLatin1String("(function ") + _name + QLatin1String("() { "));
164     _writer->replace(endOfStatement, 0, QLatin1String(" })"));
165
166     if (rewriteDump()) {
167         qWarning() << "=============================================================";
168         qWarning() << "Rewrote:";
169         qWarning() << qPrintable(code);
170     }
171
172     w.write(&code);
173
174     if (rewriteDump()) {
175         qWarning() << "To:";
176         qWarning() << qPrintable(code);
177         qWarning() << "=============================================================";
178     }
179
180     return code;
181 }
182
183 bool RewriteBinding::visit(AST::Block *ast)
184 {
185     for (AST::StatementList *it = ast->statements; it; it = it->next) {
186         if (! it->next) {
187             // we need to rewrite only the last statement of a block.
188             accept(it->statement);
189         }
190     }
191
192     return false;
193 }
194
195 bool RewriteBinding::visit(AST::ExpressionStatement *ast)
196 {
197     if (! _inLoop) {
198         unsigned startOfExpressionStatement = ast->firstSourceLocation().begin() - _position;
199         _writer->replace(startOfExpressionStatement, 0, QLatin1String("return "));
200     }
201
202     return false;
203 }
204
205 bool RewriteBinding::visit(AST::StringLiteral *ast)
206 {
207     /* When rewriting the code for bindings, we have to remove ILLEGAL JS tokens like newlines.
208        They're still in multi-line strings, because the QML parser allows them, but we have to
209        rewrite them to be JS compliant.
210
211        For performance reasons, we don't go for total correctness. \r is only replaced if a
212        \n was found (since most line endings are \n or \r\n) and QChar::LineSeparator is not
213        even considered. QTBUG-24064.
214
215        Note that rewriteSignalHandler has a function just like this one, for the same reason.
216     */
217
218     unsigned startOfString = ast->firstSourceLocation().begin() + 1 - _position;
219     unsigned stringLength = ast->firstSourceLocation().length - 2;
220
221     int lastIndex = -1;
222     bool foundNewLine = false;
223     QStringRef subStr(_code, startOfString, stringLength);
224     while (true) {
225         lastIndex = subStr.indexOf(QLatin1Char('\n'), lastIndex + 1);
226         if (lastIndex == -1)
227             break;
228         foundNewLine = true;
229         _writer->replace(startOfString+lastIndex, 1, QLatin1String("\\n"));
230     }
231
232     if (foundNewLine) {
233         while (true) {
234             lastIndex = subStr.indexOf(QLatin1Char('\r'), lastIndex + 1);
235             if (lastIndex == -1)
236                 break;
237             _writer->replace(startOfString+lastIndex, 1, QLatin1String("\\r"));
238         }
239     }
240
241     return false;
242 }
243
244 bool RewriteBinding::visit(AST::DoWhileStatement *)
245 {
246     ++_inLoop;
247     return true;
248 }
249
250 void RewriteBinding::endVisit(AST::DoWhileStatement *)
251 {
252     --_inLoop;
253 }
254
255 bool RewriteBinding::visit(AST::WhileStatement *)
256 {
257     ++_inLoop;
258     return true;
259 }
260
261 void RewriteBinding::endVisit(AST::WhileStatement *)
262 {
263     --_inLoop;
264 }
265
266 bool RewriteBinding::visit(AST::ForStatement *)
267 {
268     ++_inLoop;
269     return true;
270 }
271
272 void RewriteBinding::endVisit(AST::ForStatement *)
273 {
274     --_inLoop;
275 }
276
277 bool RewriteBinding::visit(AST::LocalForStatement *)
278 {
279     ++_inLoop;
280     return true;
281 }
282
283 void RewriteBinding::endVisit(AST::LocalForStatement *)
284 {
285     --_inLoop;
286 }
287
288 bool RewriteBinding::visit(AST::ForEachStatement *)
289 {
290     ++_inLoop;
291     return true;
292 }
293
294 void RewriteBinding::endVisit(AST::ForEachStatement *)
295 {
296     --_inLoop;
297 }
298
299 bool RewriteBinding::visit(AST::LocalForEachStatement *)
300 {
301     ++_inLoop;
302     return true;
303 }
304
305 void RewriteBinding::endVisit(AST::LocalForEachStatement *)
306 {
307     --_inLoop;
308 }
309
310 bool RewriteBinding::visit(AST::CaseBlock *ast)
311 {
312     // Process the initial sequence of the case clauses.
313     for (AST::CaseClauses *it = ast->clauses; it; it = it->next) {
314         // Return the value of the last statement in the block, if this is the last `case clause'
315         // of the switch statement.
316         bool returnTheValueOfLastStatement = (it->next == 0) && (ast->defaultClause == 0) && (ast->moreClauses == 0);
317
318         if (AST::CaseClause *clause = it->clause) {
319             accept(clause->expression);
320             rewriteCaseStatements(clause->statements, returnTheValueOfLastStatement);
321         }
322     }
323
324     // Process the default case clause
325     if (ast->defaultClause) {
326         // Return the value of the last statement in the block, if this is the last `case clause'
327         // of the switch statement.
328         bool rewriteTheLastStatement = (ast->moreClauses == 0);
329
330         rewriteCaseStatements(ast->defaultClause->statements, rewriteTheLastStatement);
331     }
332
333     // Process trailing `case clauses'
334     for (AST::CaseClauses *it = ast->moreClauses; it; it = it->next) {
335         // Return the value of the last statement in the block, if this is the last `case clause'
336         // of the switch statement.
337         bool returnTheValueOfLastStatement = (it->next == 0);
338
339         if (AST::CaseClause *clause = it->clause) {
340             accept(clause->expression);
341             rewriteCaseStatements(clause->statements, returnTheValueOfLastStatement);
342         }
343     }
344
345     return false;
346 }
347
348 void RewriteBinding::rewriteCaseStatements(AST::StatementList *statements, bool rewriteTheLastStatement)
349 {
350     for (AST::StatementList *it = statements; it; it = it->next) {
351         if (it->next && AST::cast<AST::BreakStatement *>(it->next->statement) != 0) {
352             // The value of the first statement followed by a `break'.
353             accept(it->statement);
354             break;
355         } else if (!it->next) {
356             if (rewriteTheLastStatement)
357                 accept(it->statement);
358             else if (AST::Block *block = AST::cast<AST::Block *>(it->statement))
359                 rewriteCaseStatements(block->statements, rewriteTheLastStatement);
360         }
361     }
362 }
363
364 void RewriteSignalHandler::accept(AST::Node *node)
365 {
366     AST::Node::acceptChild(node, this);
367 }
368
369 bool RewriteSignalHandler::visit(AST::StringLiteral *ast)
370 {
371     unsigned startOfExpressionStatement = ast->firstSourceLocation().begin() - _position;
372     _strStarts << startOfExpressionStatement + 1;
373     _strLens << ast->firstSourceLocation().length - 2;
374
375     return false;
376 }
377
378 void RewriteSignalHandler::rewriteMultilineStrings(QString &code)
379 {
380     QList<int> replaceR, replaceN;
381     for (int i=0; i < _strStarts.count(); i++) {
382         QStringRef curSubstr = QStringRef(&code, _strStarts[i], _strLens[i]);
383         int lastIndex = -1;
384         while (true) {
385             lastIndex = curSubstr.indexOf(QLatin1Char('\n'), lastIndex + 1);
386             if (lastIndex == -1)
387                 break;
388             replaceN << _strStarts[i]+lastIndex;
389         }
390
391         if (replaceN.count()) {
392             while (true) {
393                 lastIndex = curSubstr.indexOf(QLatin1Char('\r'), lastIndex + 1);
394                 if (lastIndex == -1)
395                     break;
396                 replaceR << _strStarts[i]+lastIndex;
397             }
398         }
399     }
400     for (int ii = replaceN.count() - 1; ii >= 0; ii--)
401         code.replace(replaceN[ii], 1, QLatin1String("\\n"));
402     if (replaceR.count())
403         for (int ii = replaceR.count() - 1; ii >= 0; ii--)
404             code.replace(replaceR[ii], 1, QLatin1String("\\r"));
405 }
406
407 QString RewriteSignalHandler::operator()(QDeclarativeJS::AST::Node *node, const QString &code, const QString &name)
408 {
409     if (rewriteDump()) {
410         qWarning() << "=============================================================";
411         qWarning() << "Rewrote:";
412         qWarning() << qPrintable(code);
413     }
414
415     QDeclarativeJS::AST::ExpressionNode *expression = node->expressionCast();
416     QDeclarativeJS::AST::Statement *statement = node->statementCast();
417     if (!expression && !statement)
418         return code;
419
420     _strStarts.clear();
421     _strLens.clear();
422     _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin();
423     accept(node);
424
425     QString rewritten = code;
426     rewriteMultilineStrings(rewritten);
427
428     rewritten = QStringLiteral("(function ") + name + QStringLiteral("() { ") + rewritten + QStringLiteral(" })");
429
430     if (rewriteDump()) {
431         qWarning() << "To:";
432         qWarning() << qPrintable(rewritten);
433         qWarning() << "=============================================================";
434     }
435
436     return rewritten;
437 }
438
439 } // namespace QDeclarativeRewrite
440
441 QT_END_NAMESPACE