diff options
-rw-r--r-- | src/qml/compiler/compiler.pri | 6 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 343 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler_p.h | 80 | ||||
-rw-r--r-- | src/qml/compiler/qv4jssimplifier.cpp | 384 | ||||
-rw-r--r-- | src/qml/compiler/qv4jssimplifier_p.h | 154 | ||||
-rw-r--r-- | tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 42 | ||||
-rw-r--r-- | tools/qmlcachegen/qmlcachegen.cpp | 8 |
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 |