qmlcompiler: Add absolute runtime function indices
We can transition to absolute indices (within the compilation unit) from the relative indices we already have in each method and script binding. Together with absolute indices, we also need to acknowledge nested closures that some AST elements might have and so also store nested runtime function indices along with the absolute ones Absolute runtime function indices (and the nested ones) map to the indices we store and use within the compilation unit at run time, which are used to dispatch to correct JavaScript call during e.g. binding evaluation - see QQmlEnginePrivate::executeRuntimeFunction() Change-Id: Ieec58fbc36563511bd9763e358cda46c67757fa9 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
b497eaf891
commit
aafc094a55
|
@ -451,6 +451,8 @@ void QQmlJSImportVisitor::endVisit(UiProgram *)
|
|||
.arg(import.startColumn),
|
||||
Log_UnusedImport, import);
|
||||
}
|
||||
|
||||
populateRuntimeFunctionIndicesForDocument();
|
||||
}
|
||||
|
||||
static QQmlJSAnnotation::Value bindingToVariant(QQmlJS::AST::Statement *statement)
|
||||
|
@ -1156,6 +1158,8 @@ void QQmlJSImportVisitor::flushPendingSignalParameters()
|
|||
|
||||
Records a JS function or a Script binding for a given \a scope. Returns an
|
||||
index of a just recorded function-or-expression.
|
||||
|
||||
\sa synthesizeCompilationUnitRuntimeFunctionIndices
|
||||
*/
|
||||
QQmlJSMetaMethod::RelativeFunctionIndex
|
||||
QQmlJSImportVisitor::addFunctionOrExpression(const QQmlJSScope::ConstPtr &scope,
|
||||
|
@ -1163,9 +1167,121 @@ QQmlJSImportVisitor::addFunctionOrExpression(const QQmlJSScope::ConstPtr &scope,
|
|||
{
|
||||
auto &array = m_functionsAndExpressions[scope];
|
||||
array.emplaceBack(name);
|
||||
if (m_currentOuterFunction.isEmpty())
|
||||
m_currentOuterFunction = name;
|
||||
return QQmlJSMetaMethod::RelativeFunctionIndex { int(array.size() - 1) };
|
||||
}
|
||||
|
||||
/*! \internal
|
||||
|
||||
(Optionally) "records" an inner function by incrementing an inner function
|
||||
counter of the corresponding outer function. When there is no outer
|
||||
function, this procedure does nothing.
|
||||
|
||||
Such recorded inner function would also be stored in the compilation unit in
|
||||
a separate compilation process and thus has to also be acknowledged here.
|
||||
|
||||
\sa addFunctionOrExpression, clearCurrentOuterFunction,
|
||||
synthesizeCompilationUnitRuntimeFunctionIndices
|
||||
*/
|
||||
void QQmlJSImportVisitor::incrementInnerFunctionCount()
|
||||
{
|
||||
// m_currentOuterFunction is set in addFunctionOrExpression()
|
||||
if (!m_currentOuterFunction.isEmpty())
|
||||
m_innerFunctions[m_currentOuterFunction]++;
|
||||
}
|
||||
|
||||
/*! \internal
|
||||
|
||||
Clears an outer function name (that acts as a flag in some sense) if that
|
||||
name matches \a name.
|
||||
|
||||
\sa addFunctionOrExpression, incrementInnerFunctionCount,
|
||||
synthesizeCompilationUnitRuntimeFunctionIndices
|
||||
*/
|
||||
void QQmlJSImportVisitor::clearCurrentOuterFunction(const QString &name)
|
||||
{
|
||||
if (name == m_currentOuterFunction)
|
||||
m_currentOuterFunction = QString {};
|
||||
}
|
||||
|
||||
/*! \internal
|
||||
|
||||
Sets absolute runtime function indices for \a scope based on \a count
|
||||
(document-level variable). Returns count incremented by the number of
|
||||
runtime functions that the current \a scope has.
|
||||
|
||||
\note Not all scopes are considered as the function is compatible with the
|
||||
compilation unit output. The runtime functions are only recorded for
|
||||
QmlIR::Object (even if they don't strictly belong to it). Thus, in
|
||||
QQmlJSScope terms, we are only interested in QML scopes, group and attached
|
||||
property scopes.
|
||||
*/
|
||||
int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices(
|
||||
const QQmlJSScope::Ptr &scope, int count) const
|
||||
{
|
||||
const auto suitableScope = [](const QQmlJSScope::Ptr &scope) {
|
||||
const auto type = scope->scopeType();
|
||||
return type == QQmlJSScope::QMLScope || type == QQmlJSScope::GroupedPropertyScope
|
||||
|| type == QQmlJSScope::AttachedPropertyScope;
|
||||
};
|
||||
|
||||
if (!suitableScope(scope))
|
||||
return count;
|
||||
|
||||
QList<QQmlJSMetaMethod::AbsoluteFunctionIndex> indices;
|
||||
auto it = m_functionsAndExpressions.constFind(scope);
|
||||
if (it == m_functionsAndExpressions.cend()) // scope has no runtime functions
|
||||
return count;
|
||||
|
||||
const auto &functionsAndExpressions = *it;
|
||||
for (const QString &functionOrExpression : functionsAndExpressions) {
|
||||
scope->addOwnRuntimeFunctionIndex(
|
||||
static_cast<QQmlJSMetaMethod::AbsoluteFunctionIndex>(count));
|
||||
++count;
|
||||
|
||||
// there are special cases: onSignal: function() { doSomethingUsefull }
|
||||
// in which we would register 2 functions in the runtime functions table
|
||||
// for the same expression. even more, we can have named and unnamed
|
||||
// closures inside a function or a script binding e.g.:
|
||||
// ```
|
||||
// function foo() {
|
||||
// var closure = () => { return 42; }; // this is an inner function
|
||||
// /* or:
|
||||
// property = Qt.binding(function() { return anotherProperty; });
|
||||
// */
|
||||
// return closure();
|
||||
// }
|
||||
// ```
|
||||
// see Codegen::defineFunction() in qv4codegen.cpp for more details
|
||||
if (m_innerFunctions.contains(functionOrExpression))
|
||||
count += m_innerFunctions[functionOrExpression];
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument() const
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// We *have* to perform DFS here: QmlIR::Object entries within the
|
||||
// QmlIR::Document are stored in the order they appear during AST traversal
|
||||
// (which does DFS) - QQmlJSScope has to acknowledge this order here,
|
||||
// otherwise we get inconsistent results
|
||||
QList<QQmlJSScope::Ptr> stack;
|
||||
stack.append(m_exportedRootScope);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
QQmlJSScope::Ptr current = stack.takeLast();
|
||||
|
||||
count = synthesizeCompilationUnitRuntimeFunctionIndices(current, count);
|
||||
|
||||
const auto &children = current->childScopes();
|
||||
std::copy(children.rbegin(), children.rend(), std::back_inserter(stack));
|
||||
}
|
||||
}
|
||||
|
||||
bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
|
||||
{
|
||||
if (m_pendingSignalHandler.isValid()) {
|
||||
|
@ -1400,12 +1516,14 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
|
|||
return true;
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::endVisit(UiPublicMember *)
|
||||
void QQmlJSImportVisitor::endVisit(UiPublicMember *publicMember)
|
||||
{
|
||||
if (m_savedBindingOuterScope) {
|
||||
m_currentScope = m_savedBindingOuterScope;
|
||||
m_savedBindingOuterScope = {};
|
||||
}
|
||||
|
||||
clearCurrentOuterFunction(publicMember->name.toString());
|
||||
}
|
||||
|
||||
bool QQmlJSImportVisitor::visit(UiRequired *required)
|
||||
|
@ -1472,6 +1590,7 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
|
|||
}
|
||||
enterEnvironment(QQmlJSScope::JSFunctionScope, name, fexpr->firstSourceLocation());
|
||||
} else {
|
||||
addFunctionOrExpression(m_currentScope, QStringLiteral("<anon>"));
|
||||
enterEnvironment(QQmlJSScope::JSFunctionScope, QStringLiteral("<anon>"),
|
||||
fexpr->firstSourceLocation());
|
||||
}
|
||||
|
@ -1479,12 +1598,14 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
|
|||
|
||||
bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
|
||||
{
|
||||
incrementInnerFunctionCount();
|
||||
visitFunctionExpressionHelper(fexpr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *)
|
||||
void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *fexpr)
|
||||
{
|
||||
clearCurrentOuterFunction(fexpr->name.toString());
|
||||
leaveEnvironment();
|
||||
}
|
||||
|
||||
|
@ -1498,12 +1619,14 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
|
|||
{
|
||||
m_logger->log(u"Declared function \"%1\""_qs.arg(fdecl->name), Log_ControlsSanity,
|
||||
fdecl->firstSourceLocation());
|
||||
incrementInnerFunctionCount();
|
||||
visitFunctionExpressionHelper(fdecl);
|
||||
return true;
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *)
|
||||
void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fdecl)
|
||||
{
|
||||
clearCurrentOuterFunction(fdecl->name.toString());
|
||||
leaveEnvironment();
|
||||
}
|
||||
|
||||
|
@ -1804,6 +1927,11 @@ void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
|
|||
m_currentScope = m_savedBindingOuterScope;
|
||||
m_savedBindingOuterScope = {};
|
||||
}
|
||||
|
||||
// clearCurrentOuterFunction() but without the name check since script
|
||||
// bindings are special (we cannot have nested script bindings, so we are
|
||||
// guaranteed to always correctly clear the name here)
|
||||
m_currentOuterFunction = QString {};
|
||||
}
|
||||
|
||||
bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
|
||||
|
|
|
@ -185,8 +185,15 @@ protected:
|
|||
// stores JS functions and Script bindings per scope (only the name). mimics
|
||||
// the content of QmlIR::Object::functionsAndExpressions
|
||||
QHash<QQmlJSScope::ConstPtr, QList<QString>> m_functionsAndExpressions;
|
||||
QString m_currentOuterFunction;
|
||||
QHash<QString, int> m_innerFunctions;
|
||||
QQmlJSMetaMethod::RelativeFunctionIndex
|
||||
addFunctionOrExpression(const QQmlJSScope::ConstPtr &scope, const QString &name);
|
||||
void incrementInnerFunctionCount();
|
||||
void clearCurrentOuterFunction(const QString &name);
|
||||
int synthesizeCompilationUnitRuntimeFunctionIndices(const QQmlJSScope::Ptr &scope,
|
||||
int count) const;
|
||||
void populateRuntimeFunctionIndicesForDocument() const;
|
||||
|
||||
QQmlJSImporter *m_importer;
|
||||
|
||||
|
|
|
@ -146,6 +146,14 @@ public:
|
|||
*/
|
||||
enum class RelativeFunctionIndex : int { Invalid = -1 };
|
||||
|
||||
/*! \internal
|
||||
|
||||
Represents an absolute JavaScript function/expression index pointing
|
||||
into the QV4::ExecutableCompilationUnit::runtimeFunctions array. Used as
|
||||
a typed alternative to int with an explicit invalid state.
|
||||
*/
|
||||
enum class AbsoluteFunctionIndex : int { Invalid = -1 };
|
||||
|
||||
QQmlJSMetaMethod() = default;
|
||||
explicit QQmlJSMetaMethod(QString name, QString returnType = QString())
|
||||
: m_name(std::move(name))
|
||||
|
|
|
@ -413,6 +413,19 @@ public:
|
|||
void setValueTypeName(const QString &name) { m_valueTypeName = name; }
|
||||
QQmlJSScope::ConstPtr valueType() const { return m_valueType; }
|
||||
|
||||
void addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index)
|
||||
{
|
||||
m_runtimeFunctionIndices.emplaceBack(index);
|
||||
}
|
||||
QQmlJSMetaMethod::AbsoluteFunctionIndex
|
||||
ownRuntimeFunctionIndex(QQmlJSMetaMethod::RelativeFunctionIndex index) const
|
||||
{
|
||||
const int i = static_cast<int>(index);
|
||||
Q_ASSERT(i >= 0);
|
||||
Q_ASSERT(i < int(m_runtimeFunctionIndices.size()));
|
||||
return m_runtimeFunctionIndices[i];
|
||||
}
|
||||
|
||||
bool isSingleton() const { return m_flags & Singleton; }
|
||||
bool isCreatable() const { return m_flags & Creatable; }
|
||||
bool isComposite() const { return m_flags & Composite; }
|
||||
|
@ -586,6 +599,9 @@ private:
|
|||
// bindings in QmlIR::Object
|
||||
QList<QmlIRCompatibilityBindingData> m_propertyBindingsArray;
|
||||
|
||||
// same as QmlIR::Object::runtimeFunctionIndices
|
||||
QList<QQmlJSMetaMethod::AbsoluteFunctionIndex> m_runtimeFunctionIndices;
|
||||
|
||||
QHash<QString, QQmlJSMetaEnum> m_enumerations;
|
||||
|
||||
QVector<QQmlJSAnnotation> m_annotations;
|
||||
|
|
|
@ -17,9 +17,18 @@ Text {
|
|||
else return "blue";
|
||||
}
|
||||
|
||||
property int newProperty: {
|
||||
var callable = () => { return 42; };
|
||||
return callable();
|
||||
}
|
||||
|
||||
// group prop script binding (value type and non value type)
|
||||
font.pixelSize: (40 + 2) / 2
|
||||
anchors.topMargin: 44 / 4
|
||||
anchors.bottomMargin: 11 + 1
|
||||
anchors.topMargin: {
|
||||
var divideBy4 = function (x) { return x / 4; };
|
||||
return divideBy4(44);
|
||||
}
|
||||
|
||||
// attached prop script binding
|
||||
Keys.enabled: root.truncated ? true : false
|
||||
|
@ -43,10 +52,12 @@ Text {
|
|||
signal mySignal2(bool x)
|
||||
onMySignal0: console.log("single line", x);
|
||||
onMySignal1: {
|
||||
console.log("mySignal1 emitted:", x);
|
||||
var identity = (x) => { return x; };
|
||||
console.log("mySignal1 emitted:", identity(x));
|
||||
}
|
||||
onMySignal2: function (x) {
|
||||
console.log("mySignal2 emitted:", x);
|
||||
var returnString = function() { return "mySignal2 emitted:"; };
|
||||
console.log(returnString(), x);
|
||||
}
|
||||
|
||||
// var property assigned a js function
|
||||
|
@ -72,7 +83,12 @@ Text {
|
|||
onDelegateSignal: { root.jsFunc(); }
|
||||
}
|
||||
|
||||
property var prop: function(x) { return x * 2; }
|
||||
component InternalInlineType : Rectangle {
|
||||
function jsFuncInsideInline2() { return 43; }
|
||||
}
|
||||
|
||||
property var funcHolder2
|
||||
funcHolder2: function(x) { return x * 2; }
|
||||
}
|
||||
|
||||
ComponentType {
|
||||
|
@ -85,4 +101,14 @@ Text {
|
|||
function jsFuncInsideDelegate(flag: bool) { return flag ? "true" : "false"; }
|
||||
}
|
||||
}
|
||||
|
||||
// function with nested one
|
||||
function jsFunctionReturningFunctions() {
|
||||
return [ function() { return 42 }, () => { return "42" } ];
|
||||
}
|
||||
|
||||
// function with Qt.binding()
|
||||
function bindText() {
|
||||
root.text = Qt.binding( function() { return jsFuncTyped("foo") + "bar"; } );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ private Q_SLOTS:
|
|||
void groupedPropertiesConsistency();
|
||||
void groupedPropertySyntax();
|
||||
void attachedProperties();
|
||||
void relativeScriptIndices();
|
||||
void scriptIndices();
|
||||
|
||||
public:
|
||||
tst_qqmljsscope()
|
||||
|
@ -431,7 +431,7 @@ inline QString getScopeName(const QQmlJSScope::ConstPtr &scope)
|
|||
return scope->baseTypeName();
|
||||
}
|
||||
|
||||
void tst_qqmljsscope::relativeScriptIndices()
|
||||
void tst_qqmljsscope::scriptIndices()
|
||||
{
|
||||
{
|
||||
QQmlEngine engine;
|
||||
|
@ -445,12 +445,40 @@ void tst_qqmljsscope::relativeScriptIndices()
|
|||
QmlIR::Document document(false); // we need QmlIR information here
|
||||
QQmlJSScope::ConstPtr root = run(u"functionAndBindingIndices.qml"_qs, &document);
|
||||
QVERIFY(root);
|
||||
QVERIFY(document.javaScriptCompilationUnit.unitData());
|
||||
|
||||
using IndexedString = std::pair<QString, int>;
|
||||
// compare {property, function}Name and relative function table index
|
||||
// between QQmlJSScope and QmlIR:
|
||||
QList<IndexedString> orderedJSScopeExpressions;
|
||||
QList<IndexedString> orderedQmlIrExpressions;
|
||||
// compare QQmlJSScope and QmlIR:
|
||||
|
||||
// {property, function}Name and relative (per-object) function table index
|
||||
QList<IndexedString> orderedJSScopeExpressionsRelative;
|
||||
QList<IndexedString> orderedQmlIrExpressionsRelative;
|
||||
// {property, function}Name and absolute (per-document) function table index
|
||||
QList<IndexedString> orderedJSScopeExpressionsAbsolute;
|
||||
QList<IndexedString> orderedQmlIrExpressionsAbsolute;
|
||||
|
||||
const auto populateQQmlJSScopeArrays =
|
||||
[&](const QQmlJSScope::ConstPtr &scope, const QString &name,
|
||||
QQmlJSMetaMethod::RelativeFunctionIndex relativeIndex) {
|
||||
orderedJSScopeExpressionsRelative.emplaceBack(name,
|
||||
static_cast<int>(relativeIndex));
|
||||
auto absoluteIndex = scope->ownRuntimeFunctionIndex(relativeIndex);
|
||||
orderedJSScopeExpressionsAbsolute.emplaceBack(name,
|
||||
static_cast<int>(absoluteIndex));
|
||||
};
|
||||
|
||||
const auto populateQmlIRArrays = [&](const QmlIR::Object *irObject, const QString &name,
|
||||
int relative) {
|
||||
orderedQmlIrExpressionsRelative.emplaceBack(name, relative);
|
||||
auto absolute = irObject->runtimeFunctionIndices.at(relative);
|
||||
orderedQmlIrExpressionsAbsolute.emplaceBack(name, absolute);
|
||||
};
|
||||
|
||||
const auto suitableScope = [](const QQmlJSScope::ConstPtr &scope) {
|
||||
const auto type = scope->scopeType();
|
||||
return type == QQmlJSScope::QMLScope || type == QQmlJSScope::GroupedPropertyScope
|
||||
|| type == QQmlJSScope::AttachedPropertyScope;
|
||||
};
|
||||
|
||||
QList<QQmlJSScope::ConstPtr> queue;
|
||||
queue.push_back(root);
|
||||
|
@ -458,28 +486,33 @@ void tst_qqmljsscope::relativeScriptIndices()
|
|||
auto current = queue.front();
|
||||
queue.pop_front();
|
||||
|
||||
const auto methods = current->ownMethods();
|
||||
for (const auto &method : methods) {
|
||||
if (method.methodType() == QQmlJSMetaMethod::Signal)
|
||||
continue;
|
||||
QString name = method.methodName();
|
||||
int index = static_cast<int>(method.jsFunctionIndex());
|
||||
QVERIFY2(index >= 0,
|
||||
qPrintable(QStringLiteral("Method %1 from %2 has no index")
|
||||
.arg(name, getScopeName(current))));
|
||||
orderedJSScopeExpressions.emplaceBack(name, index);
|
||||
}
|
||||
if (suitableScope(current)) {
|
||||
|
||||
const auto bindings = current->ownPropertyBindings();
|
||||
for (const auto &binding : bindings) {
|
||||
if (binding.bindingType() != QQmlJSMetaPropertyBinding::Script)
|
||||
continue;
|
||||
QString name = binding.propertyName();
|
||||
int index = static_cast<int>(binding.scriptIndex());
|
||||
QVERIFY2(index >= 0,
|
||||
qPrintable(QStringLiteral("Binding on property %1 from %2 has no index")
|
||||
.arg(name, getScopeName(current))));
|
||||
orderedJSScopeExpressions.emplaceBack(name, index);
|
||||
const auto methods = current->ownMethods();
|
||||
for (const auto &method : methods) {
|
||||
if (method.methodType() == QQmlJSMetaMethod::Signal)
|
||||
continue;
|
||||
QString name = method.methodName();
|
||||
auto relativeIndex = method.jsFunctionIndex();
|
||||
QVERIFY2(static_cast<int>(relativeIndex) >= 0,
|
||||
qPrintable(QStringLiteral("Method %1 from %2 has no index")
|
||||
.arg(name, getScopeName(current))));
|
||||
|
||||
populateQQmlJSScopeArrays(current, name, relativeIndex);
|
||||
}
|
||||
|
||||
const auto bindings = current->ownPropertyBindings();
|
||||
for (const auto &binding : bindings) {
|
||||
if (binding.bindingType() != QQmlJSMetaPropertyBinding::Script)
|
||||
continue;
|
||||
QString name = binding.propertyName();
|
||||
auto relativeIndex = binding.scriptIndex();
|
||||
QVERIFY2(static_cast<int>(relativeIndex) >= 0,
|
||||
qPrintable(QStringLiteral("Binding on property %1 from %2 has no index")
|
||||
.arg(name, getScopeName(current))));
|
||||
|
||||
populateQQmlJSScopeArrays(current, name, relativeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
const auto children = current->childScopes();
|
||||
|
@ -494,7 +527,7 @@ void tst_qqmljsscope::relativeScriptIndices()
|
|||
QVERIFY2(it->index >= 0,
|
||||
qPrintable(QStringLiteral("(qmlir) Method %1 from %2 has no index")
|
||||
.arg(name, objectName)));
|
||||
orderedQmlIrExpressions.emplaceBack(name, it->index);
|
||||
populateQmlIRArrays(irObject, name, it->index);
|
||||
}
|
||||
for (auto it = irObject->bindingsBegin(); it != irObject->bindingsEnd(); ++it) {
|
||||
if (it->type != QmlIR::Binding::Type_Script)
|
||||
|
@ -505,15 +538,21 @@ void tst_qqmljsscope::relativeScriptIndices()
|
|||
index >= 0,
|
||||
qPrintable(QStringLiteral("(qmlir) Binding on property %1 from %2 has no index")
|
||||
.arg(name, objectName)));
|
||||
orderedQmlIrExpressions.emplaceBack(name, index);
|
||||
populateQmlIRArrays(irObject, name, index);
|
||||
}
|
||||
}
|
||||
|
||||
auto less = [](const IndexedString &x, const IndexedString &y) { return x.first < y.first; };
|
||||
std::sort(orderedJSScopeExpressions.begin(), orderedJSScopeExpressions.end(), less);
|
||||
std::sort(orderedQmlIrExpressions.begin(), orderedQmlIrExpressions.end(), less);
|
||||
std::sort(orderedJSScopeExpressionsRelative.begin(), orderedJSScopeExpressionsRelative.end(),
|
||||
less);
|
||||
std::sort(orderedQmlIrExpressionsRelative.begin(), orderedQmlIrExpressionsRelative.end(), less);
|
||||
|
||||
QCOMPARE(orderedJSScopeExpressions, orderedQmlIrExpressions);
|
||||
std::sort(orderedJSScopeExpressionsAbsolute.begin(), orderedJSScopeExpressionsAbsolute.end(),
|
||||
less);
|
||||
std::sort(orderedQmlIrExpressionsAbsolute.begin(), orderedQmlIrExpressionsAbsolute.end(), less);
|
||||
|
||||
QCOMPARE(orderedJSScopeExpressionsRelative, orderedQmlIrExpressionsRelative);
|
||||
QCOMPARE(orderedJSScopeExpressionsAbsolute, orderedQmlIrExpressionsAbsolute);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qqmljsscope)
|
||||
|
|
Loading…
Reference in New Issue