aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/v4traced/tst_v4traced.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/v4traced/tst_v4traced.cpp')
-rw-r--r--tests/auto/qml/v4traced/tst_v4traced.cpp325
1 files changed, 325 insertions, 0 deletions
diff --git a/tests/auto/qml/v4traced/tst_v4traced.cpp b/tests/auto/qml/v4traced/tst_v4traced.cpp
new file mode 100644
index 0000000000..f82cc0ed5e
--- /dev/null
+++ b/tests/auto/qml/v4traced/tst_v4traced.cpp
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QJSEngine>
+#include <private/qjsvalue_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qv4script_p.h>
+
+class EnvVarSaver
+{
+public:
+ EnvVarSaver(const char *name, const QByteArray &newValue)
+ : _name(name)
+ {
+ _wasSet = qEnvironmentVariableIsSet(name);
+ if (_wasSet)
+ _oldValue = qgetenv(name);
+ qputenv(name, newValue);
+ }
+
+ ~EnvVarSaver()
+ {
+ if (_wasSet)
+ qputenv(_name, _oldValue);
+ else
+ qunsetenv(_name);
+ }
+
+private:
+ const char *_name;
+ bool _wasSet;
+ QByteArray _oldValue;
+};
+
+class tst_v4traced : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void collectTraces_data();
+ void collectTraces();
+
+ void binopI32deopt_data();
+ void binopI32deopt();
+
+ void calls_data();
+ void calls();
+
+ void setLookup();
+ void construct();
+};
+
+void tst_v4traced::collectTraces_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<int>("tracePointCount");
+ QTest::addColumn<int>("interestingTracePoint");
+ QTest::addColumn<quint8>("expectedBits");
+
+ QTest::newRow("int+") << "var a = 4; a + 2" << 2 << 1 << quint8(QV4::ObservedTraceValues::Integer);
+ QTest::newRow("double+") << "var a = 4.1; a + 1.9" << 2 << 1 << quint8(QV4::ObservedTraceValues::Double);
+ QTest::newRow("object+") << "var a = '4'; a + '2'" << 2 << 1 << quint8(QV4::ObservedTraceValues::Other);
+}
+
+void tst_v4traced::collectTraces()
+{
+ QFETCH(QString, code);
+ QFETCH(int, tracePointCount);
+ QFETCH(int, interestingTracePoint);
+ QFETCH(quint8, expectedBits);
+
+ EnvVarSaver forceInterpreter("QV4_FORCE_INTERPRETER", "1");
+ EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
+
+ QV4::ExecutionEngine vm;
+ QV4::Scope scope(&vm);
+ QV4::ScopedContext ctx(scope, vm.rootContext());
+ QV4::ScopedValue result(scope);
+ QScopedPointer<QV4::Script> script;
+ script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "collectTraces"));
+ script->parseAsBinding = false;
+
+ QVERIFY(!scope.engine->hasException);
+ script->parse();
+ QVERIFY(!scope.engine->hasException);
+
+ QVERIFY(script->function()->tracingEnabled());
+ result = script->run();
+ QVERIFY(!scope.engine->hasException);
+
+ QCOMPARE(int(script->function()->compiledFunction->nTraceInfos), tracePointCount);
+ QCOMPARE(*script->function()->traceInfo(interestingTracePoint), expectedBits);
+}
+
+void tst_v4traced::binopI32deopt_data()
+{
+ QTest::addColumn<QString>("operand");
+ QTest::addColumn<int>("int_arg1");
+ QTest::addColumn<int>("int_arg2");
+ QTest::addColumn<int>("int_result");
+ QTest::addColumn<QString>("other_arg1");
+ QTest::addColumn<QString>("other_arg2");
+ QTest::addColumn<double>("other_result");
+
+ QTest::newRow("+") << "+" << 1 << 2 << 3 << "1.1" << "1.9" << 3.0;
+ QTest::newRow("-") << "-" << 3 << 2 << 1 << "3.1" << "2.1" << 1.0;
+ QTest::newRow("*") << "*" << 2 << 3 << 6 << "2.1" << "1.9" << 3.99;
+ QTest::newRow("/") << "/" << 6 << 3 << 2 << "6.6" << "3.3" << 2.0;
+
+ QTest::newRow("&") << "&" << 6 << 3 << 2 << "'6'" << "'3'" << 2.0;
+ QTest::newRow("|") << "|" << 6 << 3 << 7 << "'6'" << "'3'" << 7.0;
+ QTest::newRow("^") << "^" << 6 << 3 << 5 << "'6'" << "'3'" << 5.0;
+
+ QTest::newRow("<<") << "<<" << 5 << 1 << 10 << "'5'" << "'1'" << 10.0;
+ QTest::newRow(">>") << ">>" << -1 << 1 << -1 << "'-1'" << "'1'" << -1.0;
+ QTest::newRow(">>>") << ">>>" << -1 << 1 << 0x7FFFFFFF << "'-1'" << "'1'" << 2147483647.0;
+
+ QTest::newRow("==") << "==" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
+ QTest::newRow("!=") << "!=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
+ QTest::newRow("<" ) << "<" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
+ QTest::newRow("<=") << "<=" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
+ QTest::newRow(">" ) << ">" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
+ QTest::newRow(">=") << ">=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
+}
+
+void tst_v4traced::binopI32deopt()
+{
+ QFETCH(QString, operand);
+ QFETCH(int, int_arg1);
+ QFETCH(int, int_arg2);
+ QFETCH(int, int_result);
+ QFETCH(QString, other_arg1);
+ QFETCH(QString, other_arg2);
+ QFETCH(double, other_result);
+
+ QString func = QStringLiteral("function binopI32(a, b) { return a %1 b }").arg(operand);
+ QString intCall = QStringLiteral("binopI32(%1, %2)").arg(int_arg1).arg(int_arg2);
+ QString otherCall = QStringLiteral("binopI32(%1, %2)").arg(other_arg1).arg(other_arg2);
+
+ QJSEngine engine;
+ engine.evaluate(func);
+
+ QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // interpret + trace
+ QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // jit
+ QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // deopt
+ QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // retrace
+ QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // rejit
+}
+
+void tst_v4traced::calls_data()
+{
+ QTest::addColumn<QString>("call");
+
+ QTest::newRow("callGlobalLookup") << "globalLookup";
+ QTest::newRow("callPropertyLookup") << "obj.propertyLookup";
+}
+
+class Calls
+{
+public:
+ static int callCount;
+
+ static QV4::ReturnedValue doSomething(const QV4::FunctionObject */*o*/,
+ const QV4::Value */*thiz*/,
+ const QV4::Value *argv, int argc)
+ {
+ ++callCount;
+
+ if (argc == 0)
+ return QV4::Encode(42);
+
+ int prod = 1;
+ for (int i = 0; i < argc; ++i) {
+ Q_ASSERT(argv[i].isInteger());
+ prod *= argv[i].int_32();
+ }
+ return QV4::Encode(prod);
+ }
+};
+
+int Calls::callCount = 0;
+
+void tst_v4traced::calls()
+{
+ QFETCH(QString, call);
+
+ EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
+ EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
+
+ QV4::ExecutionEngine vm;
+ QV4::Scope scope(&vm);
+ QV4::ScopedContext ctx(scope, vm.rootContext());
+ vm.globalObject->defineDefaultProperty(QLatin1String("globalLookup"),
+ Calls::doSomething);
+ QV4::ScopedObject obj(scope, vm.newObject());
+ vm.globalObject->defineDefaultProperty(QLatin1String("obj"), obj);
+ obj->defineDefaultProperty("propertyLookup", Calls::doSomething);
+
+ QString code = QStringLiteral(
+ "function doCalls() {\n"
+ " if (%1() != 42) return false\n"
+ " if (%1(21, 2) != 42) return false\n"
+ " if (%1(2, 3, 7) != 42) return false\n"
+ " return true\n"
+ "}\n"
+ "var result = true\n"
+ "for (var i = 0; i < 10; ++i) {\n"
+ " if (!doCalls()) { result = false; break }"
+ "}\n"
+ "result\n").arg(call);
+ QScopedPointer<QV4::Script> script;
+ script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "call"));
+ script->parseAsBinding = false;
+
+ QVERIFY(!scope.engine->hasException);
+ script->parse();
+ QVERIFY(!scope.engine->hasException);
+
+ Calls::callCount = 0;
+ QV4::ScopedValue result(scope, script->run());
+ QVERIFY(!scope.engine->hasException);
+
+ QVERIFY(result->isBoolean());
+ QVERIFY(result->booleanValue());
+ QCOMPARE(Calls::callCount, 30);
+}
+
+void tst_v4traced::setLookup()
+{
+ EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
+ EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
+
+ QV4::ExecutionEngine vm;
+ QV4::Scope scope(&vm);
+ QV4::ScopedContext ctx(scope, vm.rootContext());
+ QV4::ScopedObject obj(scope, vm.newObject());
+ vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj);
+ obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32));
+
+ QString code = QStringLiteral(
+ "function doit() {\n"
+ " ++oracle.answer\n"
+ "}\n"
+ "for (var i = 0; i < 10; ++i) doit()\n"
+ "oracle.answer\n");
+ QScopedPointer<QV4::Script> script;
+ script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup"));
+ script->parseAsBinding = false;
+
+ QVERIFY(!scope.engine->hasException);
+ script->parse();
+ QVERIFY(!scope.engine->hasException);
+
+ QV4::ScopedValue result(scope, script->run());
+ QVERIFY(!scope.engine->hasException);
+
+ QVERIFY(result->isInteger());
+ QCOMPARE(result->int_32(), 42);
+}
+
+void tst_v4traced::construct()
+{
+ EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
+ EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
+
+ QV4::ExecutionEngine vm;
+ QV4::Scope scope(&vm);
+ QV4::ScopedContext ctx(scope, vm.rootContext());
+ QV4::ScopedObject obj(scope, vm.newObject());
+ vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj);
+ obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32));
+
+ QString code = QStringLiteral(
+ "function doit() {\n"
+ " this.arr = new Array()\n"
+ " this.arr[0] = 0\n"
+ " this.arr[1] = 1\n"
+ " this.arr[2] = 2\n"
+ "}\n"
+ "var o\n"
+ "for (var i = 0; i < 10; ++i) o = new doit()\n"
+ "o.arr\n");
+ QScopedPointer<QV4::Script> script;
+ script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup"));
+ script->parseAsBinding = false;
+
+ QVERIFY(!scope.engine->hasException);
+ script->parse();
+ QVERIFY(!scope.engine->hasException);
+
+ QV4::ScopedValue result(scope, script->run());
+ QVERIFY(!scope.engine->hasException);
+
+ QVERIFY(result->as<QV4::ArrayObject>());
+}
+
+QTEST_MAIN(tst_v4traced)
+
+#include "tst_v4traced.moc"