aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/compiler.pri6
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp343
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h80
-rw-r--r--src/qml/compiler/qv4jssimplifier.cpp384
-rw-r--r--src/qml/compiler/qv4jssimplifier_p.h154
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp42
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp8
7 files changed, 591 insertions, 426 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index fa66d3a6e3..871f28f2d0 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -10,7 +10,8 @@ HEADERS += \
$$PWD/qv4isel_util_p.h \
$$PWD/qv4ssa_p.h \
$$PWD/qqmlirbuilder_p.h \
- $$PWD/qqmltypecompiler_p.h
+ $$PWD/qqmltypecompiler_p.h \
+ $$PWD/qv4jssimplifier_p.h
SOURCES += \
$$PWD/qv4compileddata.cpp \
@@ -19,7 +20,8 @@ SOURCES += \
$$PWD/qv4isel_p.cpp \
$$PWD/qv4jsir.cpp \
$$PWD/qv4ssa.cpp \
- $$PWD/qqmlirbuilder.cpp
+ $$PWD/qqmlirbuilder.cpp \
+ $$PWD/qv4jssimplifier.cpp
!qmldevtools_build {
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index a3b8784fc8..d1d22be0ac 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -47,6 +47,7 @@
#include <private/qv4ssa_p.h>
#include "qqmlpropertycachecreator_p.h"
+#include "qv4jssimplifier_p.h"
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -144,7 +145,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
if (!jsCodeGen.generateCodeForComponents())
return nullptr;
- QQmlJavaScriptBindingExpressionSimplificationPass pass(this);
+ QQmlJavaScriptBindingExpressionSimplificationPass pass(document->objects, &document->jsModule, &document->jsGenerator);
pass.reduceTranslationBindings();
QV4::ExecutionEngine *v4 = engine->v4engine();
@@ -1429,344 +1430,4 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
}
}
-QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler)
- : QQmlCompilePass(typeCompiler)
- , qmlObjects(*typeCompiler->qmlObjects())
- , jsModule(typeCompiler->jsIRModule())
-{
-
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings()
-{
- for (int i = 0; i < qmlObjects.count(); ++i)
- reduceTranslationBindings(i);
- if (!irFunctionsToRemove.isEmpty()) {
- QQmlIRFunctionCleanser cleanser(compiler, irFunctionsToRemove);
- cleanser.clean();
- }
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex)
-{
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
-
- for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
- continue;
-
- const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex);
- QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex);
- if (simplifyBinding(irFunction, binding)) {
- irFunctionsToRemove.append(irFunctionIndex);
- jsModule->functions[irFunctionIndex] = 0;
- delete irFunction;
- }
- }
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move)
-{
- QV4::IR::Temp *target = move->target->asTemp();
- if (!target || target->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
-
- if (QV4::IR::Call *call = move->source->asCall()) {
- if (QV4::IR::Name *n = call->base->asName()) {
- if (n->builtin == QV4::IR::Name::builtin_invalid) {
- visitFunctionCall(n->id, call->args, target);
- return;
- }
- }
- discard();
- return;
- }
-
- if (QV4::IR::Name *n = move->source->asName()) {
- if (n->builtin == QV4::IR::Name::builtin_qml_context
- || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) {
- // these are free of side-effects
- return;
- }
- discard();
- return;
- }
-
- if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) {
- discard();
- return;
- }
-
- _temps[target->index] = move->source;
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target)
-{
- // more than one function call?
- if (_nameOfFunctionCalled) {
- discard();
- return;
- }
-
- _nameOfFunctionCalled = name;
-
- _functionParameters.clear();
- while (args) {
- int slot;
- if (QV4::IR::Temp *param = args->expr->asTemp()) {
- if (param->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
- slot = param->index;
- _functionParameters.append(slot);
- } else if (QV4::IR::Const *param = args->expr->asConst()) {
- slot = --_synthesizedConsts;
- Q_ASSERT(!_temps.contains(slot));
- _temps[slot] = param;
- _functionParameters.append(slot);
- }
- args = args->next;
- }
-
- _functionCallReturnValue = target->index;
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret)
-{
- // nothing initialized earlier?
- if (_returnValueOfBindingExpression != -1) {
- discard();
- return;
- }
- QV4::IR::Temp *target = ret->expr->asTemp();
- if (!target || target->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
- _returnValueOfBindingExpression = target->index;
-}
-
-bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding)
-{
- _canSimplify = true;
- _nameOfFunctionCalled = 0;
- _functionParameters.clear();
- _functionCallReturnValue = -1;
- _temps.clear();
- _returnValueOfBindingExpression = -1;
- _synthesizedConsts = 0;
-
- // It would seem unlikely that function with some many basic blocks (after optimization)
- // consists merely of a qsTr call or a constant value return ;-)
- if (function->basicBlockCount() > 10)
- return false;
-
- for (QV4::IR::BasicBlock *bb : function->basicBlocks()) {
- for (QV4::IR::Stmt *s : bb->statements()) {
- visit(s);
- if (!_canSimplify)
- return false;
- }
- }
-
- if (_returnValueOfBindingExpression == -1)
- return false;
-
- if (_nameOfFunctionCalled) {
- if (_functionCallReturnValue != _returnValueOfBindingExpression)
- return false;
- return detectTranslationCallAndConvertBinding(binding);
- }
-
- return false;
-}
-
-bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding)
-{
- if (*_nameOfFunctionCalled == QLatin1String("qsTr")) {
- QString translation;
- QV4::CompiledData::TranslationData translationData;
- translationData.number = -1;
- translationData.commentIndex = 0; // empty string
-
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- translation = *stringParam->value;
-
- ++param;
- if (param != end) {
- stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
- translationData.commentIndex = compiler->registerString(*stringParam->value);
- ++param;
-
- if (param != end) {
- QV4::IR::Const *constParam = _temps[*param]->asConst();
- if (!constParam || constParam->type != QV4::IR::SInt32Type)
- return false;
-
- translationData.number = int(constParam->value);
- ++param;
- }
- }
-
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_Translation;
- binding->stringIndex = compiler->registerString(translation);
- binding->value.translationData = translationData;
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) {
- QString id;
- QV4::CompiledData::TranslationData translationData;
- translationData.number = -1;
- translationData.commentIndex = 0; // empty string, but unused
-
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- id = *stringParam->value;
-
- ++param;
- if (param != end) {
- QV4::IR::Const *constParam = _temps[*param]->asConst();
- if (!constParam || constParam->type != QV4::IR::SInt32Type)
- return false;
-
- translationData.number = int(constParam->value);
- ++param;
- }
-
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_TranslationById;
- binding->stringIndex = compiler->registerString(id);
- binding->value.translationData = translationData;
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) {
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- ++param;
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_String;
- binding->stringIndex = compiler->registerString(*stringParam->value);
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) {
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- ++param;
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- ++param;
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_String;
- binding->stringIndex = compiler->registerString(*stringParam->value);
- return true;
- }
- return false;
-}
-
-QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove)
- : QQmlCompilePass(typeCompiler)
- , module(typeCompiler->jsIRModule())
- , functionsToRemove(functionsToRemove)
-{
-}
-
-void QQmlIRFunctionCleanser::clean()
-{
- QVector<QV4::IR::Function*> newFunctions;
- newFunctions.reserve(module->functions.count() - functionsToRemove.count());
-
- newFunctionIndices.resize(module->functions.count());
-
- for (int i = 0; i < module->functions.count(); ++i) {
- QV4::IR::Function *f = module->functions.at(i);
- Q_ASSERT(f || functionsToRemove.contains(i));
- if (f) {
- newFunctionIndices[i] = newFunctions.count();
- newFunctions << f;
- }
- }
-
- module->functions = newFunctions;
-
- for (QV4::IR::Function *function : qAsConst(module->functions)) {
- for (QV4::IR::BasicBlock *block : function->basicBlocks()) {
- for (QV4::IR::Stmt *s : block->statements()) {
- visit(s);
- }
- }
- }
-
- for (QmlIR::Object *obj : qAsConst(*compiler->qmlObjects())) {
- for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i)
- obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)];
- }
-}
-
-void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s)
-{
-
- switch (s->stmtKind) {
- case QV4::IR::Stmt::PhiStmt:
- // nothing to do
- break;
- default:
- STMT_VISIT_ALL_KINDS(s);
- break;
- }
-}
-
-void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e)
-{
- switch (e->exprKind) {
- case QV4::IR::Expr::ClosureExpr: {
- auto closure = e->asClosure();
- closure->value = newFunctionIndices.at(closure->value);
- } break;
- default:
- EXPR_VISIT_ALL_KINDS(e);
- break;
- }
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index 11261e3099..79fc073d8b 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -345,86 +345,6 @@ private:
const QQmlPropertyCacheVector * const propertyCaches;
};
-class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass
-{
-public:
- QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler);
-
- void reduceTranslationBindings();
-
-private:
- void reduceTranslationBindings(int objectIndex);
-
- void visit(QV4::IR::Stmt *s)
- {
- switch (s->stmtKind) {
- case QV4::IR::Stmt::MoveStmt:
- visitMove(s->asMove());
- break;
- case QV4::IR::Stmt::RetStmt:
- visitRet(s->asRet());
- break;
- case QV4::IR::Stmt::CJumpStmt:
- discard();
- break;
- case QV4::IR::Stmt::ExpStmt:
- discard();
- break;
- case QV4::IR::Stmt::JumpStmt:
- break;
- case QV4::IR::Stmt::PhiStmt:
- break;
- }
- }
-
- void visitMove(QV4::IR::Move *move);
- void visitRet(QV4::IR::Ret *ret);
-
- void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target);
-
- void discard() { _canSimplify = false; }
-
- bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding);
- bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding);
-
- const QVector<QmlIR::Object*> &qmlObjects;
- QV4::IR::Module *jsModule;
-
- bool _canSimplify;
- const QString *_nameOfFunctionCalled;
- QVector<int> _functionParameters;
- int _functionCallReturnValue;
-
- QHash<int, QV4::IR::Expr*> _temps;
- int _returnValueOfBindingExpression;
- int _synthesizedConsts;
-
- QVector<int> irFunctionsToRemove;
-};
-
-class QQmlIRFunctionCleanser : public QQmlCompilePass
-{
-public:
- QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove);
-
- void clean();
-
-private:
- virtual void visitMove(QV4::IR::Move *s) {
- visit(s->source);
- visit(s->target);
- }
-
- void visit(QV4::IR::Stmt *s);
- void visit(QV4::IR::Expr *e);
-
-private:
- QV4::IR::Module *module;
- const QVector<int> &functionsToRemove;
-
- QVector<int> newFunctionIndices;
-};
-
QT_END_NAMESPACE
#endif // QQMLTYPECOMPILER_P_H
diff --git a/src/qml/compiler/qv4jssimplifier.cpp b/src/qml/compiler/qv4jssimplifier.cpp
new file mode 100644
index 0000000000..7d09218fe6
--- /dev/null
+++ b/src/qml/compiler/qv4jssimplifier.cpp
@@ -0,0 +1,384 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4jssimplifier_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator)
+ : qmlObjects(qmlObjects)
+ , jsModule(jsModule)
+ , unitGenerator(unitGenerator)
+{
+
+}
+
+void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings()
+{
+ for (int i = 0; i < qmlObjects.count(); ++i)
+ reduceTranslationBindings(i);
+ if (!irFunctionsToRemove.isEmpty()) {
+ QQmlIRFunctionCleanser cleanser(jsModule, qmlObjects, irFunctionsToRemove);
+ cleanser.clean();
+ }
+}
+
+void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex)
+{
+ const QmlIR::Object *obj = qmlObjects.at(objectIndex);
+
+ for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Script)
+ continue;
+
+ const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex);
+ QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex);
+ if (simplifyBinding(irFunction, binding)) {
+ irFunctionsToRemove.append(irFunctionIndex);
+ jsModule->functions[irFunctionIndex] = 0;
+ delete irFunction;
+ }
+ }
+}
+
+void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move)
+{
+ QV4::IR::Temp *target = move->target->asTemp();
+ if (!target || target->kind != QV4::IR::Temp::VirtualRegister) {
+ discard();
+ return;
+ }
+
+ if (QV4::IR::Call *call = move->source->asCall()) {
+ if (QV4::IR::Name *n = call->base->asName()) {
+ if (n->builtin == QV4::IR::Name::builtin_invalid) {
+ visitFunctionCall(n->id, call->args, target);
+ return;
+ }
+ }
+ discard();
+ return;
+ }
+
+ if (QV4::IR::Name *n = move->source->asName()) {
+ if (n->builtin == QV4::IR::Name::builtin_qml_context
+ || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) {
+ // these are free of side-effects
+ return;
+ }
+ discard();
+ return;
+ }
+
+ if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) {
+ discard();
+ return;
+ }
+
+ _temps[target->index] = move->source;
+}
+
+void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target)
+{
+ // more than one function call?
+ if (_nameOfFunctionCalled) {
+ discard();
+ return;
+ }
+
+ _nameOfFunctionCalled = name;
+
+ _functionParameters.clear();
+ while (args) {
+ int slot;
+ if (QV4::IR::Temp *param = args->expr->asTemp()) {
+ if (param->kind != QV4::IR::Temp::VirtualRegister) {
+ discard();
+ return;
+ }
+ slot = param->index;
+ _functionParameters.append(slot);
+ } else if (QV4::IR::Const *param = args->expr->asConst()) {
+ slot = --_synthesizedConsts;
+ Q_ASSERT(!_temps.contains(slot));
+ _temps[slot] = param;
+ _functionParameters.append(slot);
+ }
+ args = args->next;
+ }
+
+ _functionCallReturnValue = target->index;
+}
+
+void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret)
+{
+ // nothing initialized earlier?
+ if (_returnValueOfBindingExpression != -1) {
+ discard();
+ return;
+ }
+ QV4::IR::Temp *target = ret->expr->asTemp();
+ if (!target || target->kind != QV4::IR::Temp::VirtualRegister) {
+ discard();
+ return;
+ }
+ _returnValueOfBindingExpression = target->index;
+}
+
+bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding)
+{
+ _canSimplify = true;
+ _nameOfFunctionCalled = 0;
+ _functionParameters.clear();
+ _functionCallReturnValue = -1;
+ _temps.clear();
+ _returnValueOfBindingExpression = -1;
+ _synthesizedConsts = 0;
+
+ // It would seem unlikely that function with some many basic blocks (after optimization)
+ // consists merely of a qsTr call or a constant value return ;-)
+ if (function->basicBlockCount() > 10)
+ return false;
+
+ for (QV4::IR::BasicBlock *bb : function->basicBlocks()) {
+ for (QV4::IR::Stmt *s : bb->statements()) {
+ visit(s);
+ if (!_canSimplify)
+ return false;
+ }
+ }
+
+ if (_returnValueOfBindingExpression == -1)
+ return false;
+
+ if (_nameOfFunctionCalled) {
+ if (_functionCallReturnValue != _returnValueOfBindingExpression)
+ return false;
+ return detectTranslationCallAndConvertBinding(binding);
+ }
+
+ return false;
+}
+
+bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding)
+{
+ if (*_nameOfFunctionCalled == QLatin1String("qsTr")) {
+ QString translation;
+ QV4::CompiledData::TranslationData translationData;
+ translationData.number = -1;
+ translationData.commentIndex = 0; // empty string
+
+ QVector<int>::ConstIterator param = _functionParameters.constBegin();
+ QVector<int>::ConstIterator end = _functionParameters.constEnd();
+ if (param == end)
+ return false;
+
+ QV4::IR::String *stringParam = _temps[*param]->asString();
+ if (!stringParam)
+ return false;
+
+ translation = *stringParam->value;
+
+ ++param;
+ if (param != end) {
+ stringParam = _temps[*param]->asString();
+ if (!stringParam)
+ return false;
+ translationData.commentIndex = unitGenerator->registerString(*stringParam->value);
+ ++param;
+
+ if (param != end) {
+ QV4::IR::Const *constParam = _temps[*param]->asConst();
+ if (!constParam || constParam->type != QV4::IR::SInt32Type)
+ return false;
+
+ translationData.number = int(constParam->value);
+ ++param;
+ }
+ }
+
+ if (param != end)
+ return false;
+
+ binding->type = QV4::CompiledData::Binding::Type_Translation;
+ binding->stringIndex = unitGenerator->registerString(translation);
+ binding->value.translationData = translationData;
+ return true;
+ } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) {
+ QString id;
+ QV4::CompiledData::TranslationData translationData;
+ translationData.number = -1;
+ translationData.commentIndex = 0; // empty string, but unused
+
+ QVector<int>::ConstIterator param = _functionParameters.constBegin();
+ QVector<int>::ConstIterator end = _functionParameters.constEnd();
+ if (param == end)
+ return false;
+
+ QV4::IR::String *stringParam = _temps[*param]->asString();
+ if (!stringParam)
+ return false;
+
+ id = *stringParam->value;
+
+ ++param;
+ if (param != end) {
+ QV4::IR::Const *constParam = _temps[*param]->asConst();
+ if (!constParam || constParam->type != QV4::IR::SInt32Type)
+ return false;
+
+ translationData.number = int(constParam->value);
+ ++param;
+ }
+
+ if (param != end)
+ return false;
+
+ binding->type = QV4::CompiledData::Binding::Type_TranslationById;
+ binding->stringIndex = unitGenerator->registerString(id);
+ binding->value.translationData = translationData;
+ return true;
+ } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) {
+ QVector<int>::ConstIterator param = _functionParameters.constBegin();
+ QVector<int>::ConstIterator end = _functionParameters.constEnd();
+ if (param == end)
+ return false;
+
+ QV4::IR::String *stringParam = _temps[*param]->asString();
+ if (!stringParam)
+ return false;
+
+ ++param;
+ if (param != end)
+ return false;
+
+ binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->stringIndex = unitGenerator->registerString(*stringParam->value);
+ return true;
+ } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) {
+ QVector<int>::ConstIterator param = _functionParameters.constBegin();
+ QVector<int>::ConstIterator end = _functionParameters.constEnd();
+ if (param == end)
+ return false;
+
+ ++param;
+ if (param == end)
+ return false;
+
+ QV4::IR::String *stringParam = _temps[*param]->asString();
+ if (!stringParam)
+ return false;
+
+ ++param;
+ if (param != end)
+ return false;
+
+ binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->stringIndex = unitGenerator->registerString(*stringParam->value);
+ return true;
+ }
+ return false;
+}
+
+QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object *> &qmlObjects, const QVector<int> &functionsToRemove)
+ : module(module)
+ , qmlObjects(qmlObjects)
+ , functionsToRemove(functionsToRemove)
+{
+}
+
+void QQmlIRFunctionCleanser::clean()
+{
+ QVector<QV4::IR::Function*> newFunctions;
+ newFunctions.reserve(module->functions.count() - functionsToRemove.count());
+
+ newFunctionIndices.resize(module->functions.count());
+
+ for (int i = 0; i < module->functions.count(); ++i) {
+ QV4::IR::Function *f = module->functions.at(i);
+ Q_ASSERT(f || functionsToRemove.contains(i));
+ if (f) {
+ newFunctionIndices[i] = newFunctions.count();
+ newFunctions << f;
+ }
+ }
+
+ module->functions = newFunctions;
+
+ for (QV4::IR::Function *function : qAsConst(module->functions)) {
+ for (QV4::IR::BasicBlock *block : function->basicBlocks()) {
+ for (QV4::IR::Stmt *s : block->statements()) {
+ visit(s);
+ }
+ }
+ }
+
+ for (QmlIR::Object *obj : qmlObjects) {
+ for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i)
+ obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)];
+ }
+}
+
+void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s)
+{
+
+ switch (s->stmtKind) {
+ case QV4::IR::Stmt::PhiStmt:
+ // nothing to do
+ break;
+ default:
+ STMT_VISIT_ALL_KINDS(s);
+ break;
+ }
+}
+
+void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e)
+{
+ switch (e->exprKind) {
+ case QV4::IR::Expr::ClosureExpr: {
+ auto closure = e->asClosure();
+ closure->value = newFunctionIndices.at(closure->value);
+ } break;
+ default:
+ EXPR_VISIT_ALL_KINDS(e);
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4jssimplifier_p.h b/src/qml/compiler/qv4jssimplifier_p.h
new file mode 100644
index 0000000000..ae8d74135c
--- /dev/null
+++ b/src/qml/compiler/qv4jssimplifier_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4JSSIMPLIFIER
+#define QV4JSSIMPLIFIER
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+
+#include "qqmlirbuilder_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QmlIR {
+struct Document;
+}
+
+namespace QV4 {
+namespace CompiledData {
+struct QmlUnit;
+struct Location;
+}
+}
+
+class QQmlJavaScriptBindingExpressionSimplificationPass
+{
+public:
+ QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator);
+
+ void reduceTranslationBindings();
+
+private:
+ void reduceTranslationBindings(int objectIndex);
+
+ void visit(QV4::IR::Stmt *s)
+ {
+ switch (s->stmtKind) {
+ case QV4::IR::Stmt::MoveStmt:
+ visitMove(s->asMove());
+ break;
+ case QV4::IR::Stmt::RetStmt:
+ visitRet(s->asRet());
+ break;
+ case QV4::IR::Stmt::CJumpStmt:
+ discard();
+ break;
+ case QV4::IR::Stmt::ExpStmt:
+ discard();
+ break;
+ case QV4::IR::Stmt::JumpStmt:
+ break;
+ case QV4::IR::Stmt::PhiStmt:
+ break;
+ }
+ }
+
+ void visitMove(QV4::IR::Move *move);
+ void visitRet(QV4::IR::Ret *ret);
+
+ void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target);
+
+ void discard() { _canSimplify = false; }
+
+ bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding);
+ bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding);
+
+ const QVector<QmlIR::Object*> &qmlObjects;
+ QV4::IR::Module *jsModule;
+ QV4::Compiler::JSUnitGenerator *unitGenerator;
+
+ bool _canSimplify;
+ const QString *_nameOfFunctionCalled;
+ QVector<int> _functionParameters;
+ int _functionCallReturnValue;
+
+ QHash<int, QV4::IR::Expr*> _temps;
+ int _returnValueOfBindingExpression;
+ int _synthesizedConsts;
+
+ QVector<int> irFunctionsToRemove;
+};
+
+class QQmlIRFunctionCleanser
+{
+public:
+ QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object*> &qmlObjects, const QVector<int> &functionsToRemove);
+
+ void clean();
+
+private:
+ virtual void visitMove(QV4::IR::Move *s) {
+ visit(s->source);
+ visit(s->target);
+ }
+
+ void visit(QV4::IR::Stmt *s);
+ void visit(QV4::IR::Expr *e);
+
+private:
+ QV4::IR::Module *module;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QVector<int> &functionsToRemove;
+
+ QVector<int> newFunctionIndices;
+};
+
+QT_END_NAMESPACE
+
+#endif // QV4JSSIMPLIFIER
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index 27bb2348b8..b7e616a050 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -42,6 +42,7 @@ private slots:
void initTestCase();
void loadGeneratedFile();
+ void translationExpressionSupport();
};
// A wrapper around QQmlComponent to ensure the temporary reference counts
@@ -116,6 +117,47 @@ void tst_qmlcachegen::loadGeneratedFile()
QCOMPARE(obj->property("value").toInt(), 42);
}
+void tst_qmlcachegen::translationExpressionSupport()
+{
+ QTemporaryDir tempDir;
+ QVERIFY(tempDir.isValid());
+
+ const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) {
+ QFile f(tempDir.path() + '/' + fileName);
+ const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ Q_ASSERT(ok);
+ f.write(contents);
+ return f.fileName();
+ };
+
+ const QString testFilePath = writeTempFile("test.qml", "import QtQml.Models 2.2\n"
+ "import QtQml 2.2\n"
+ "QtObject {\n"
+ " property ListModel model: ListModel {\n"
+ " ListElement {\n"
+ " text: qsTr(\"All\")\n"
+ " }\n"
+ " ListElement {\n"
+ " text: QT_TR_NOOP(\"Ok\")\n"
+ " }\n"
+ " }\n"
+ " property string text: model.get(0).text + \" \" + model.get(1).text\n"
+ "}");
+
+
+ QVERIFY(generateCache(testFilePath));
+
+ const QString cacheFilePath = testFilePath + QLatin1Char('c');
+ QVERIFY(QFile::exists(cacheFilePath));
+ QVERIFY(QFile::remove(testFilePath));
+
+ QQmlEngine engine;
+ CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("text").toString(), QString("All Ok"));
+}
+
QTEST_GUILESS_MAIN(tst_qmlcachegen)
#include "tst_qmlcachegen.moc"
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index 3c25f5347b..b201176d5e 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -37,6 +37,7 @@
#include <private/qqmlirbuilder_p.h>
#include <private/qv4isel_moth_p.h>
#include <private/qqmljsparser_p.h>
+#include <private/qv4jssimplifier_p.h>
QT_BEGIN_NAMESPACE
@@ -174,7 +175,10 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi
QmlIR::QmlUnitGenerator generator;
- // ### translation binding simplification
+ {
+ QQmlJavaScriptBindingExpressionSimplificationPass pass(irDocument.objects, &irDocument.jsModule, &irDocument.jsGenerator);
+ pass.reduceTranslationBindings();
+ }
QV4::ExecutableAllocator allocator;
QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator));
@@ -264,8 +268,6 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil
QmlIR::QmlUnitGenerator generator;
- // ### translation binding simplification
-
QV4::ExecutableAllocator allocator;
QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator));
// Disable lookups in non-standalone (aka QML) mode