diff options
author | Luca Di Sera <luca.disera@qt.io> | 2024-04-16 14:36:01 +0200 |
---|---|---|
committer | Luca Di Sera <luca.disera@qt.io> | 2024-04-18 22:30:24 +0200 |
commit | bd6df61c81cbb419514fee0c9fdcfe50b5fca98b (patch) | |
tree | 466c7bb7169360b3bc4caf1de2b8b862f439d6d1 | |
parent | 217dae934785afb9d754bfe92933111809ab0f24 (diff) |
qmlsc: Support Math static properties
The javascript Math object presents some static properties that allow
access to some general mathematical constants.
Currently, `qmlsc` will refuse to generate optimized code for those
properties when used in a binding, for example given:
```
import QtQuick
Window {
width: 200 * Math.PI
}
```
`qmlsc` will report a warning and will not generate code for the "width"
binding.
To allow `qmlsc` to generate optimized code for such cases, the handling
of `Math` related properties lookups is now specialized.
`QQmlJSTypePropagator::propagatePropertyLookup`, which is called when
dealing with an access such as `Math.PI`, was modified to consider the
result type a "double" when dealing with properties on the `Math`
object.
`QQmlJSCodeGenerator::generate_GetLookupHelper`, which generates the
code that provides a value for the property access, was modified to
special case lookups on the `Math` object.
If a property is being looked up on the `Math` object, `qmlsc` will now
generate a direct assignment for the output variable to a constant value
that is suitable for the accessed property.
A test was added to ensure that the snippet from the bug-report now
compiles without warnings.
A test was added to ensure that the properties from the Math global
object have an approximately correct value.
Fixes: QTBUG-113150
Change-Id: I8903794fc8ce2b55532a4706e1bda07a7b73f311
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 30 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 16 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml | 17 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml | 13 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 21 |
6 files changed, 99 insertions, 0 deletions
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index 2fb6b74cba..d75c48232a 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -1245,6 +1245,36 @@ void QQmlJSCodeGenerator::generate_GetLookupHelper(int index) return; } + if (m_typeResolver->equals(m_state.accumulatorOut().scopeType(), mathObject())) { + QString name = m_jsUnitGenerator->lookupName(index); + + double value{}; + if (name == u"E") { + value = std::exp(1.0); + } else if (name == u"LN10") { + value = log(10.0); + } else if (name == u"LN2") { + value = log(2.0); + } else if (name == u"LOG10E") { + value = log10(std::exp(1.0)); + } else if (name == u"LOG2E") { + value = log2(std::exp(1.0)); + } else if (name == u"PI") { + value = 3.14159265358979323846; + } else if (name == u"SQRT1_2") { + value = std::sqrt(0.5); + } else if (name == u"SQRT2") { + value = std::sqrt(2.0); + } else { + Q_UNREACHABLE(); + } + + m_body += m_state.accumulatorVariableOut + u" = "_s + + conversion(m_typeResolver->realType(), m_state.accumulatorOut(), toNumericString(value)) + + u";\n"_s; + return; + } + if (m_state.accumulatorOut().isImportNamespace()) { Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectModulePrefix); // If we have an object module prefix, we need to pass through the original object. diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index 1d7e3eb36e..69df1ebcb3 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -882,6 +882,22 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName, } if (m_state.accumulatorOut().isProperty()) { + const QQmlJSScope::ConstPtr mathObject + = m_typeResolver->jsGlobalObject()->property(u"Math"_s).type(); + if (m_typeResolver->registerContains(m_state.accumulatorIn(), mathObject)) { + QQmlJSMetaProperty prop; + prop.setPropertyName(propertyName); + prop.setTypeName(u"double"_s); + prop.setType(m_typeResolver->realType()); + setAccumulator( + QQmlJSRegisterContent::create( + m_typeResolver->realType(), prop, m_state.accumulatorIn().resultLookupIndex(), lookupIndex, + QQmlJSRegisterContent::GenericObjectProperty, mathObject) + ); + + return; + } + if (m_typeResolver->registerContains( m_state.accumulatorOut(), m_typeResolver->voidType())) { setError(u"Type %1 does not have a property %2 for reading"_s diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 483a606040..4d9b6aea41 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -195,6 +195,7 @@ set(qml_files math.qml mathMinMax.qml mathOperations.qml + mathStaticProperties.qml mergedObjectRead.qml mergedObjectWrite.qml methodOnListLookup.qml @@ -227,6 +228,7 @@ set(qml_files popContextAfterRet.qml prefixedMetaType.qml pressAndHoldButton.qml + qtbug113150.qml reduceWithNullThis.qml readEnumFromInstance.qml readonlyListProperty.qml diff --git a/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml b/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml new file mode 100644 index 0000000000..fad74a28bd --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +pragma Strict + +import QML + +QtObject { + property double e: Math.E + property double ln10: Math.LN10 + property double ln2: Math.LN2 + property double log10e: Math.LOG10E + property double log2e: Math.LOG2E + property double pi: Math.PI + property double sqrt1_2: Math.SQRT1_2 + property double sqrt2: Math.SQRT2 +} diff --git a/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml b/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml new file mode 100644 index 0000000000..c7103eaf05 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml @@ -0,0 +1,13 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +pragma Strict + +import QtQuick + +Window { + // If static properties of the Math global object are not directly + // supported, a warning should be issued in turn failing the build + // due to `pragma Strict`. + width: 200 * Math.PI +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index b8bb660516..ae8ef49b22 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -160,6 +160,7 @@ private slots: void math(); void mathMinMax(); void mathOperations(); + void mathStaticProperties(); void mergedObjectReadWrite(); void methodOnListLookup(); void methods(); @@ -3215,6 +3216,26 @@ void tst_QmlCppCodegen::mathOperations() } } +void tst_QmlCppCodegen::mathStaticProperties() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathStaticProperties.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + // Approximate values based on + // https://262.ecma-international.org/14.0/#sec-value-properties-of-the-math-object + QCOMPARE(object->property("e").toDouble(), 2.7182818284590452354); + QCOMPARE(object->property("ln10").toDouble(), 2.302585092994046); + QCOMPARE(object->property("ln2").toDouble(), 0.6931471805599453); + QCOMPARE(object->property("log10e").toDouble(), 0.4342944819032518); + QCOMPARE(object->property("log2e").toDouble(), 1.4426950408889634); + QCOMPARE(object->property("pi").toDouble(), 3.1415926535897932); + QCOMPARE(object->property("sqrt1_2").toDouble(), 0.7071067811865476); + QCOMPARE(object->property("sqrt2").toDouble(), 1.4142135623730951); +} + void tst_QmlCppCodegen::mergedObjectReadWrite() { QQmlEngine e; |