aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/jsruntime.pri20
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4engine.cpp41
-rw-r--r--src/qml/jsruntime/qv4executableallocator.cpp10
-rw-r--r--src/qml/jsruntime/qv4executableallocator_p.h1
-rw-r--r--src/qml/jsruntime/qv4function.cpp67
-rw-r--r--src/qml/jsruntime/qv4function_p.h52
-rw-r--r--src/qml/jsruntime/qv4functiontable_noop.cpp65
-rw-r--r--src/qml/jsruntime/qv4functiontable_p.h75
-rw-r--r--src/qml/jsruntime/qv4functiontable_unix.cpp99
-rw-r--r--src/qml/jsruntime/qv4functiontable_win64.cpp153
-rw-r--r--src/qml/jsruntime/qv4global_p.h14
-rw-r--r--src/qml/jsruntime/qv4math_p.h27
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp50
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h2
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp13
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp153
17 files changed, 797 insertions, 51 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 5ec55b960b..a24ee0a188 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -147,7 +147,8 @@ HEADERS += \
$$PWD/qv4value_p.h \
$$PWD/qv4string_p.h \
$$PWD/qv4util_p.h \
- $$PWD/qv4value_p.h
+ $$PWD/qv4value_p.h \
+ $$PWD/qv4functiontable_p.h
SOURCES += \
$$PWD/qv4engine.cpp \
@@ -156,6 +157,23 @@ SOURCES += \
$$PWD/qv4value.cpp \
$$PWD/qv4executableallocator.cpp
+qmldevtools_build {
+ SOURCES += \
+ $$PWD/qv4functiontable_noop.cpp
+} else:win32 {
+ !winrt:equals(QT_ARCH, x86_64) {
+ SOURCES += \
+ $$PWD/qv4functiontable_win64.cpp
+ } else {
+ SOURCES += \
+ $$PWD/qv4functiontable_noop.cpp
+ }
+} else {
+ SOURCES += \
+ $$PWD/qv4functiontable_unix.cpp
+}
+
+
valgrind {
DEFINES += V4_USE_VALGRIND
}
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index a13fb37a52..21c6a5d06b 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -670,17 +670,17 @@ static inline QString ToTimeString(double t)
static inline QString ToLocaleString(double t)
{
- return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate);
+ return ToDateTime(t, Qt::LocalTime).toString(Qt::DefaultLocaleShortDate);
}
static inline QString ToLocaleDateString(double t)
{
- return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate);
+ return ToDateTime(t, Qt::LocalTime).date().toString(Qt::DefaultLocaleShortDate);
}
static inline QString ToLocaleTimeString(double t)
{
- return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate);
+ return ToDateTime(t, Qt::LocalTime).time().toString(Qt::DefaultLocaleShortDate);
}
static double getLocalTZA()
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index bd1124beb6..4c5e124cb0 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -50,6 +50,7 @@
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
+#include <QLoggingCategory>
#ifndef V4_BOOTSTRAP
@@ -134,6 +135,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcTracingAll, "qt.v4.tracing.all")
+
using namespace QV4;
#ifndef V4_BOOTSTRAP
@@ -652,6 +655,13 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
ExecutionEngine::~ExecutionEngine()
{
+ if (Q_UNLIKELY(lcTracingAll().isDebugEnabled())) {
+ for (auto cu : compilationUnits) {
+ for (auto f : qAsConst(cu->runtimeFunctions))
+ qCDebug(lcTracingAll).noquote().nospace() << f->traceInfoToString();
+ }
+ }
+
modules.clear();
delete m_multiplyWrappedQObjects;
m_multiplyWrappedQObjects = nullptr;
@@ -1620,6 +1630,22 @@ static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVaria
return a.asReturnedValue();
}
+// Converts a QSequentialIterable to JS.
+// The result is a new Array object with length equal to the length
+// of the QSequentialIterable, and the elements being the QSequentialIterable's
+// elements converted to JS, recursively.
+static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst)
+{
+ QV4::Scope scope(v4);
+ QV4::ScopedArrayObject a(scope, v4->newArrayObject());
+ a->arrayReserve(lst.size());
+ QV4::ScopedValue v(scope);
+ for (int i = 0; i < lst.size(); i++)
+ a->arrayPut(i, (v = variantToJS(v4, lst.at(i))));
+ a->setArrayLengthUnchecked(lst.size());
+ return a.asReturnedValue();
+}
+
// Converts a QVariantMap to JS.
// The result is a new Object object with property names being
// the keys of the QVariantMap, and values being the values of
@@ -1724,9 +1750,18 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data)
return QV4::Encode::null();
}
QMetaType mt(type);
- if (mt.flags() & QMetaType::IsGadget) {
- Q_ASSERT(mt.metaObject());
- return QV4::QQmlValueTypeWrapper::create(this, QVariant(type, data), mt.metaObject(), type);
+ if (auto metaObject = mt.metaObject()) {
+ auto flags = mt.flags();
+ if (flags & QMetaType::IsGadget) {
+ return QV4::QQmlValueTypeWrapper::create(this, QVariant(type, data), metaObject, type);
+ } else if (flags & QMetaType::PointerToQObject) {
+ return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data));
+ }
+ }
+ if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) {
+ auto v = QVariant(type, data);
+ QSequentialIterable lst = v.value<QSequentialIterable>();
+ return sequentialIterableToJS(this, lst);
}
// Fall back to wrapping in a QVariant.
return QV4::Encode(newVariantObject(QVariant(type, data)));
diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp
index 6f04a712e6..c836d121e3 100644
--- a/src/qml/jsruntime/qv4executableallocator.cpp
+++ b/src/qml/jsruntime/qv4executableallocator.cpp
@@ -38,17 +38,23 @@
****************************************************************************/
#include "qv4executableallocator_p.h"
+#include "qv4functiontable_p.h"
#include <wtf/StdLibExtras.h>
#include <wtf/PageAllocation.h>
using namespace QV4;
-void *ExecutableAllocator::Allocation::start() const
+void *ExecutableAllocator::Allocation::exceptionHandler() const
{
return reinterpret_cast<void*>(addr);
}
+void *ExecutableAllocator::Allocation::start() const
+{
+ return reinterpret_cast<void*>(addr + exceptionHandlerSize());
+}
+
void ExecutableAllocator::Allocation::deallocate(ExecutableAllocator *allocator)
{
if (isValid())
@@ -162,7 +168,7 @@ ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size)
Allocation *allocation = nullptr;
// Code is best aligned to 16-byte boundaries.
- size = WTF::roundUpToMultipleOf(16, size);
+ size = WTF::roundUpToMultipleOf(16, size + exceptionHandlerSize());
QMultiMap<size_t, Allocation*>::Iterator it = freeAllocations.lowerBound(size);
if (it != freeAllocations.end()) {
diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h
index 375c9a365f..013c6d7120 100644
--- a/src/qml/jsruntime/qv4executableallocator_p.h
+++ b/src/qml/jsruntime/qv4executableallocator_p.h
@@ -86,6 +86,7 @@ public:
, free(true)
{}
+ void *exceptionHandler() const;
void *start() const;
void invalidate() { addr = 0; }
bool isValid() const { return addr != 0; }
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 51a9b92967..1bd4329fe8 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -46,6 +46,7 @@
#include "qv4lookup_p.h"
#include <private/qv4mm_p.h>
#include <private/qv4identifiertable_p.h>
+#include <private/qv4functiontable_p.h>
#include <assembler/MacroAssemblerCodeRef.h>
#include <private/qv4vme_moth_p.h>
#include <private/qqmlglobal_p.h>
@@ -72,12 +73,29 @@ ReturnedValue Function::call(const Value *thisObject, const Value *argv, int arg
return result;
}
+Function *Function::create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function)
+{
+ quint16 traceSlotCount = 0;
+#if QT_CONFIG(qml_tracing)
+ traceSlotCount = function->nTraceInfos == CompiledData::Function::NoTracing()
+ ? 1
+ : function->nTraceInfos;
+#endif
+ quint8 *storage = new quint8[sizeof(Function) + traceSlotCount];
+ return new(storage) Function(engine, unit, function);
+}
+
+void Function::destroy()
+{
+ delete[] reinterpret_cast<quint8 *>(this);
+}
+
Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function)
- : compiledFunction(function)
- , compilationUnit(unit)
+ : FunctionData(unit)
+ , compiledFunction(function)
, codeData(function->code())
- , jittedCode(nullptr)
- , codeRef(nullptr)
+ , jittedCode(nullptr)
+ , codeRef(nullptr)
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
@@ -93,11 +111,21 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
internalClass = ic->d();
nFormals = compiledFunction->nFormals;
+
+#if QT_CONFIG(qml_tracing)
+ if (tracingEnabled()) {
+ for (uint i = 0; i < function->nTraceInfos; ++i)
+ *traceInfo(i) = 0;
+ }
+#endif
}
Function::~Function()
{
- delete codeRef;
+ if (codeRef) {
+ destroyFunctionTable(this, codeRef);
+ delete codeRef;
+ }
}
void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters)
@@ -144,9 +172,38 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
nFormals = parameters.size();
}
+QString Function::prettyName(const Function *function, const void *code)
+{
+ QString prettyName = function ? function->name()->toQString() : QString();
+ if (prettyName.isEmpty()) {
+ prettyName = QString::number(reinterpret_cast<quintptr>(code), 16);
+ prettyName.prepend(QLatin1String("QV4::Function(0x"));
+ prettyName.append(QLatin1Char(')'));
+ }
+ return prettyName;
+}
+
QQmlSourceLocation Function::sourceLocation() const
{
return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
}
+QString Function::traceInfoToString()
+{
+ QString info = QLatin1String("=== Trace information for ") + name()->toQString() + QLatin1Char(':');
+ if (!tracingEnabled())
+ return info + QStringLiteral(" disabled. Interpreter call count: %1\n").arg(interpreterCallCount);
+ if (compiledFunction->nTraceInfos == 0)
+ return info + QLatin1String(" none.\n");
+
+ info += QLatin1Char('\n');
+ for (uint i = 0, ei = compiledFunction->nTraceInfos; i < ei; ++i) {
+ auto bits = QString::number(*traceInfo(i), 2);
+ if (bits.size() < 8)
+ bits.prepend(QString(8 - bits.size(), '0'));
+ info += QStringLiteral(" %1: %2\n").arg(QString::number(i), bits);
+ }
+ return info;
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 62c5d24fc4..f8125a58f8 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -64,10 +64,24 @@ struct QQmlSourceLocation;
namespace QV4 {
-struct Q_QML_EXPORT Function {
- const CompiledData::Function *compiledFunction;
+struct Q_QML_EXPORT FunctionData {
CompiledData::CompilationUnit *compilationUnit;
+ FunctionData(CompiledData::CompilationUnit *compilationUnit)
+ : compilationUnit(compilationUnit)
+ {}
+};
+// Make sure this class can be accessed through offsetof (done by the assemblers):
+Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value);
+
+struct Q_QML_EXPORT Function : public FunctionData {
+private:
+ Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
+ ~Function();
+
+public:
+ const CompiledData::Function *compiledFunction;
+
ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context);
const char *codeData;
@@ -82,15 +96,18 @@ struct Q_QML_EXPORT Function {
int interpreterCallCount = 0;
bool isEval = false;
- Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
- ~Function();
+ static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
+ void destroy();
// used when dynamically assigning signal handlers (QQmlConnection)
void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters);
- inline Heap::String *name() {
+ inline Heap::String *name() const {
return compilationUnit->runtimeStrings[compiledFunction->nameIndex];
}
+
+ static QString prettyName(const Function *function, const void *address);
+
inline QString sourceFile() const { return compilationUnit->fileName(); }
inline QUrl finalUrl() const { return compilationUnit->finalUrl(); }
@@ -106,6 +123,31 @@ struct Q_QML_EXPORT Function {
return nullptr;
return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex];
}
+
+ Q_NEVER_INLINE QString traceInfoToString();
+
+ quint8 *traceInfo(uint i)
+ {
+#if QT_CONFIG(qml_tracing)
+ Q_ASSERT((tracingEnabled() && i < traceInfoCount()) || (i == 0));
+ return reinterpret_cast<quint8 *>(this) + sizeof(Function) + i;
+#else
+ Q_UNUSED(i);
+ return nullptr;
+#endif
+ }
+
+ quint32 traceInfoCount() const
+ { return compiledFunction->nTraceInfos; }
+
+ bool tracingEnabled() const
+ {
+#if QT_CONFIG(qml_tracing)
+ return traceInfoCount() != CompiledData::Function::NoTracing();
+#else
+ return false;
+#endif
+ }
};
}
diff --git a/src/qml/jsruntime/qv4functiontable_noop.cpp b/src/qml/jsruntime/qv4functiontable_noop.cpp
new file mode 100644
index 0000000000..31c198eb00
--- /dev/null
+++ b/src/qml/jsruntime/qv4functiontable_noop.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module 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 "qv4functiontable_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+void generateFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef)
+{
+ Q_UNUSED(function);
+ Q_UNUSED(codeRef);
+}
+
+void destroyFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef)
+{
+ Q_UNUSED(function);
+ Q_UNUSED(codeRef);
+}
+
+size_t exceptionHandlerSize()
+{
+ return 0;
+}
+
+} // QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4functiontable_p.h b/src/qml/jsruntime/qv4functiontable_p.h
new file mode 100644
index 0000000000..69e3d2bdd5
--- /dev/null
+++ b/src/qml/jsruntime/qv4functiontable_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module 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 QV4FUNCTIONTABLE_P_H
+#define QV4FUNCTIONTABLE_P_H
+
+//
+// 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 "qv4global_p.h"
+
+namespace JSC {
+class MacroAssemblerCodeRef;
+}
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Function;
+
+void generateFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef);
+void destroyFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef);
+
+size_t exceptionHandlerSize();
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4FUNCTIONTABLE_P_H
diff --git a/src/qml/jsruntime/qv4functiontable_unix.cpp b/src/qml/jsruntime/qv4functiontable_unix.cpp
new file mode 100644
index 0000000000..25b5c27161
--- /dev/null
+++ b/src/qml/jsruntime/qv4functiontable_unix.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module 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 "qv4functiontable_p.h"
+#include "qv4function_p.h"
+
+#include <assembler/MacroAssemblerCodeRef.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+void generateFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef)
+{
+ // This implements writing of JIT'd addresses so that perf can find the
+ // symbol names.
+ //
+ // Perf expects the mapping to be in a certain place and have certain
+ // content, for more information, see:
+ // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
+ static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP");
+ if (Q_UNLIKELY(doProfile)) {
+ static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map")
+ .arg(QCoreApplication::applicationPid()));
+ static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly);
+ if (!isOpen) {
+ qWarning("QV4::JIT::Assembler: Cannot write perf map file.");
+ doProfile = false;
+ } else {
+ const void *address = codeRef->code().executableAddress();
+ perfMapFile.write(QByteArray::number(reinterpret_cast<quintptr>(address), 16));
+ perfMapFile.putChar(' ');
+ perfMapFile.write(QByteArray::number(static_cast<qsizetype>(codeRef->size()), 16));
+ perfMapFile.putChar(' ');
+ perfMapFile.write(Function::prettyName(function, address).toUtf8());
+ perfMapFile.putChar('\n');
+ perfMapFile.flush();
+ }
+ }
+}
+
+void destroyFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef)
+{
+ Q_UNUSED(function);
+ Q_UNUSED(codeRef);
+
+ // It's not advisable to remove things from the perf map file, as it's primarily used to analyze
+ // a trace after the application has terminated. We want to know about all functions that were
+ // ever jitted then. If the memory ranges overlap, we will have a problem when analyzing the
+ // trace. The JIT should try to avoid this.
+}
+
+size_t exceptionHandlerSize()
+{
+ return 0;
+}
+
+} // QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4functiontable_win64.cpp b/src/qml/jsruntime/qv4functiontable_win64.cpp
new file mode 100644
index 0000000000..fc13dc2602
--- /dev/null
+++ b/src/qml/jsruntime/qv4functiontable_win64.cpp
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module 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 "qv4functiontable_p.h"
+
+#include <assembler/MacroAssemblerCodeRef.h>
+
+#include <QtCore/qdebug.h>
+
+#include <windows.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+enum UnwindOpcode: UINT8
+{
+ UWOP_PUSH_NONVOL = 0, /* info == register number */
+ UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
+ UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
+ UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
+ UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
+ UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
+ UWOP_SAVE_XMM128 = 8, /* info == XMM reg number, offset in next slot */
+ UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
+ UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
+};
+
+enum Register : UINT8
+{
+ RAX = 0,
+ RCX,
+ RDX,
+ RBX,
+ RSP,
+ RBP,
+ RSI,
+ RDI,
+ NONE = 15
+};
+
+struct UnwindCode
+{
+ UnwindCode(UINT8 offset, UnwindOpcode operation, Register info)
+ : offset(offset), operation(operation), info(info)
+ {}
+
+ UINT8 offset;
+ UINT8 operation: 4;
+ UINT8 info: 4;
+};
+
+struct UnwindInfo
+{
+ UINT8 Version : 3;
+ UINT8 Flags : 5;
+ UINT8 SizeOfProlog;
+ UINT8 CountOfUnwindCodes;
+ UINT8 FrameRegister : 4;
+ UINT8 FrameRegisterOffset : 4;
+ UnwindCode UnwindCodes[2];
+};
+
+struct ExceptionHandlerRecord
+{
+ RUNTIME_FUNCTION handler;
+ UnwindInfo info;
+};
+
+void generateFunctionTable(Function *, JSC::MacroAssemblerCodeRef *codeRef)
+{
+ ExceptionHandlerRecord *record = reinterpret_cast<ExceptionHandlerRecord *>(
+ codeRef->executableMemory()->exceptionHandler());
+
+ record->info.Version = 1;
+ record->info.Flags = 0;
+ record->info.SizeOfProlog = 4;
+ record->info.CountOfUnwindCodes = 2;
+ record->info.FrameRegister = RBP;
+ record->info.FrameRegisterOffset = 0;
+
+ // Push frame pointer
+ record->info.UnwindCodes[1] = UnwindCode(1, UWOP_PUSH_NONVOL, RBP);
+ // Set frame pointer from stack pointer
+ record->info.UnwindCodes[0] = UnwindCode(4, UWOP_SET_FPREG, NONE);
+
+ const quintptr codeStart = quintptr(codeRef->code().executableAddress());
+ const quintptr codeSize = codeRef->size();
+
+ record->handler.BeginAddress = DWORD(codeStart - quintptr(record));
+ record->handler.EndAddress = DWORD(codeStart + codeSize - quintptr(record));
+ record->handler.UnwindData = offsetof(ExceptionHandlerRecord, info);
+
+ if (!RtlAddFunctionTable(&record->handler, 1, DWORD64(record))) {
+ const unsigned int errorCode = GetLastError();
+ qWarning() << "Failed to install win64 unwind hook. Error code:" << errorCode;
+ }
+}
+
+void destroyFunctionTable(Function *, JSC::MacroAssemblerCodeRef *codeRef)
+{
+ ExceptionHandlerRecord *record = reinterpret_cast<ExceptionHandlerRecord *>(
+ codeRef->executableMemory()->exceptionHandler());
+ if (!RtlDeleteFunctionTable(&record->handler)) {
+ const unsigned int errorCode = GetLastError();
+ qWarning() << "Failed to remove win64 unwind hook. Error code:" << errorCode;
+ }
+}
+
+size_t exceptionHandlerSize()
+{
+ return sizeof(ExceptionHandlerRecord);
+}
+
+} // QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 58bffdf2d1..d47393b3bb 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -280,6 +280,20 @@ struct IdentifierTable;
class RegExpCache;
class MultiplyWrappedQObjectMap;
+enum class ObservedTraceValues : quint8 {
+ Integer = 1 << 0,
+ Boolean = 1 << 1,
+ Double = 1 << 2,
+ Other = 1 << 3,
+ TypeMask = Integer | Boolean | Double | Other,
+
+ TruePathTaken = 1 << 0,
+ FalsePathTaken = 1 << 1,
+
+ ArrayWasAccessed = 1 << 7,
+ ArrayAccessNeededFallback = 1 << 6,
+};
+
enum PropertyFlag {
Attr_Data = 0,
Attr_Accessor = 0x1,
diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h
index 90246c4229..a60a49a811 100644
--- a/src/qml/jsruntime/qv4math_p.h
+++ b/src/qml/jsruntime/qv4math_p.h
@@ -66,27 +66,42 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b)
+static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b, quint8 *traceInfo = nullptr)
{
int result;
- if (Q_UNLIKELY(add_overflow(a, b, &result)))
+ if (Q_UNLIKELY(add_overflow(a, b, &result))) {
+ if (traceInfo)
+ *traceInfo |= quint8(QV4::ObservedTraceValues::Double);
return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue();
+ }
+ if (traceInfo)
+ *traceInfo |= quint8(QV4::ObservedTraceValues::Integer);
return Value::fromInt32(result).asReturnedValue();
}
-static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b)
+static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b, quint8 *traceInfo = nullptr)
{
int result;
- if (Q_UNLIKELY(sub_overflow(a, b, &result)))
+ if (Q_UNLIKELY(sub_overflow(a, b, &result))) {
+ if (traceInfo)
+ *traceInfo |= quint8(QV4::ObservedTraceValues::Double);
return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue();
+ }
+ if (traceInfo)
+ *traceInfo |= quint8(QV4::ObservedTraceValues::Integer);
return Value::fromInt32(result).asReturnedValue();
}
-static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b)
+static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b, quint8 *traceInfo = nullptr)
{
int result;
- if (Q_UNLIKELY(mul_overflow(a, b, &result)))
+ if (Q_UNLIKELY(mul_overflow(a, b, &result))) {
+ if (traceInfo)
+ *traceInfo |= quint8(QV4::ObservedTraceValues::Double);
return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue();
+ }
+ if (traceInfo)
+ *traceInfo |= quint8(QV4::ObservedTraceValues::Integer);
return Value::fromInt32(result).asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 6a242ba5f6..f7c339dc26 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -704,6 +704,30 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &
return getElementFallback(engine, object, index);
}
+ReturnedValue Runtime::method_loadElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot)
+{
+ *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed);
+ if (index.isPositiveInt()) {
+ uint idx = static_cast<uint>(index.int_32());
+ if (Heap::Base *b = object.heapObject()) {
+ if (b->internalClass->vtable->isObject) {
+ Heap::Object *o = static_cast<Heap::Object *>(b);
+ if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
+ Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
+ if (idx < s->values.size)
+ if (!s->data(idx).isEmpty())
+ return s->data(idx).asReturnedValue();
+ }
+ }
+ }
+ *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback);
+ return getElementIntFallback(engine, object, idx);
+ }
+
+ *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback);
+ return getElementFallback(engine, object, index);
+}
+
static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
Scope scope(engine);
@@ -759,6 +783,30 @@ void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object,
engine->throwTypeError();
}
+void Runtime::method_storeElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot)
+{
+ *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed);
+ if (index.isPositiveInt()) {
+ uint idx = static_cast<uint>(index.int_32());
+ if (Heap::Base *b = object.heapObject()) {
+ if (b->internalClass->vtable->isObject) {
+ Heap::Object *o = static_cast<Heap::Object *>(b);
+ if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
+ Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
+ if (idx < s->values.size) {
+ s->setData(engine, idx, value);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback);
+ if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict())
+ engine->throwTypeError();
+}
+
ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator)
{
Scope scope(engine);
@@ -1405,7 +1453,7 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base,
if (!f) {
QString error = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
+ .arg(name->toQString(),
base->toQStringNoThrow());
return engine->throwTypeError(error);
}
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 4b3905c56f..ceec13a3bb 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -113,9 +113,11 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \
F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \
+ F(void, storeElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot)) \
F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \
F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \
F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \
+ F(ReturnedValue, loadElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot)) \
F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \
F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \
F(ReturnedValue, loadSuperConstructor, (ExecutionEngine *engine, const Value &t)) \
diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp
index ef0877dbd0..e4d8bcaafc 100644
--- a/src/qml/jsruntime/qv4variantobject.cpp
+++ b/src/qml/jsruntime/qv4variantobject.cpp
@@ -138,11 +138,14 @@ ReturnedValue VariantPrototype::method_toString(const FunctionObject *b, const V
const VariantObject *o = thisObject->as<QV4::VariantObject>();
if (!o)
RETURN_UNDEFINED();
- QString result = o->d()->data().toString();
- if (result.isEmpty() && !o->d()->data().canConvert(QVariant::String)) {
- result = QLatin1String("QVariant(")
- + QLatin1String(o->d()->data().typeName())
- + QLatin1Char(')');
+ const QVariant variant = o->d()->data();
+ QString result = variant.toString();
+ if (result.isEmpty() && !variant.canConvert(QVariant::String)) {
+ QDebug dbg(&result);
+ dbg << variant;
+ // QDebug appends a space, we're not interested in continuing the stream so we chop it off.
+ // Can't use nospace() because it would affect the debug-stream operator of the variant.
+ result.chop(1);
}
return Encode(v4->newString(result));
}
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index ea2217499f..dd712a15d6 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -346,6 +346,70 @@ static struct InstrCount {
if (engine->hasException) \
goto handleUnwind
+static inline void traceJumpTakesTruePath(bool truePathTaken, Function *f, int slot)
+{
+#if QT_CONFIG(qml_tracing)
+ quint8 *traceInfo = f->traceInfo(slot);
+ Q_ASSERT(traceInfo);
+ *traceInfo |= truePathTaken ? quint8(ObservedTraceValues::TruePathTaken)
+ : quint8(ObservedTraceValues::FalsePathTaken);
+#else
+ Q_UNUSED(truePathTaken);
+ Q_UNUSED(f);
+ Q_UNUSED(slot);
+#endif
+}
+
+static inline void traceValue(ReturnedValue acc, Function *f, int slot)
+{
+#if QT_CONFIG(qml_tracing)
+ quint8 *traceInfo = f->traceInfo(slot);
+ Q_ASSERT(traceInfo);
+ switch (Primitive::fromReturnedValue(acc).type()) {
+ case QV4::Value::Integer_Type:
+ *traceInfo |= quint8(ObservedTraceValues::Integer);
+ break;
+ case QV4::Value::Boolean_Type:
+ *traceInfo |= quint8(ObservedTraceValues::Boolean);
+ break;
+ case QV4::Value::Double_Type:
+ *traceInfo |= quint8(ObservedTraceValues::Double);
+ break;
+ default:
+ *traceInfo |= quint8(ObservedTraceValues::Other);
+ break;
+ }
+#else
+ Q_UNUSED(acc);
+ Q_UNUSED(f);
+ Q_UNUSED(slot);
+#endif
+}
+
+static inline void traceDoubleValue(Function *f, int slot)
+{
+#if QT_CONFIG(qml_tracing)
+ quint8 *traceInfo = f->traceInfo(slot);
+ Q_ASSERT(traceInfo);
+ *traceInfo |= quint8(ObservedTraceValues::Double);
+#else
+ Q_UNUSED(f);
+ Q_UNUSED(slot);
+#endif
+}
+
+static inline void traceOtherValue(Function *f, int slot)
+{
+#if QT_CONFIG(qml_tracing)
+ quint8 *traceInfo = f->traceInfo(slot);
+ Q_ASSERT(traceInfo);
+ *traceInfo |= quint8(ObservedTraceValues::Other);
+#else
+ Q_UNUSED(f);
+ Q_UNUSED(slot);
+#endif
+}
+
static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
{
Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d();
@@ -453,6 +517,11 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
QV4::ReturnedValue acc = accumulator.asReturnedValue();
Value *stack = reinterpret_cast<Value *>(frame->jsFrame);
+ if (function->tracingEnabled()) {
+ for (int i = 0; i < int(function->nFormals); ++i)
+ traceValue(frame->jsFrame->argument(i), function, i);
+ }
+
MOTH_JUMP_TABLE;
for (;;) {
@@ -511,6 +580,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
acc = cc->locals[index].asReturnedValue();
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadLocal)
MOTH_BEGIN_INSTR(StoreLocal)
@@ -523,6 +593,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(LoadScopedLocal)
auto cc = getScope(stack, scope);
acc = cc->locals[index].asReturnedValue();
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadScopedLocal)
MOTH_BEGIN_INSTR(StoreScopedLocal)
@@ -547,6 +618,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
STORE_IP();
acc = Runtime::method_loadName(engine, name);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadName)
MOTH_BEGIN_INSTR(LoadGlobalLookup)
@@ -554,6 +626,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
acc = l->globalGetter(l, engine);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadGlobalLookup)
MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
@@ -561,6 +634,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
acc = l->qmlContextPropertyGetter(l, engine, nullptr);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(StoreNameStrict)
@@ -580,14 +654,25 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(LoadElement)
STORE_IP();
STORE_ACC();
+#if QT_CONFIG(qml_tracing)
+ acc = Runtime::method_loadElement_traced(engine, STACK_VALUE(base), accumulator, function->traceInfo(traceSlot));
+ traceValue(acc, function, traceSlot);
+#else
+ Q_UNUSED(traceSlot);
acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator);
+#endif
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(StoreElement)
STORE_IP();
STORE_ACC();
+#if QT_CONFIG(qml_tracing)
+ Runtime::method_storeElement_traced(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator, function->traceInfo(traceSlot));
+#else
+ Q_UNUSED(traceSlot);
Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator);
+#endif
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreElement)
@@ -596,6 +681,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
STORE_ACC();
acc = Runtime::method_loadProperty(engine, accumulator, name);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
@@ -614,6 +700,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
@@ -687,6 +774,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
Value undef = Value::undefinedValue();
acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallWithReceiver)
@@ -698,12 +786,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
}
acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallWithReceiver)
MOTH_BEGIN_INSTR(CallProperty)
STORE_IP();
acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallProperty)
MOTH_BEGIN_INSTR(CallPropertyLookup)
@@ -731,42 +821,49 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallElement)
STORE_IP();
acc = Runtime::method_callElement(engine, stack + base, STACK_VALUE(index), stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallElement)
MOTH_BEGIN_INSTR(CallName)
STORE_IP();
acc = Runtime::method_callName(engine, name, stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallName)
MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
STORE_IP();
acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallPossiblyDirectEval)
MOTH_BEGIN_INSTR(CallGlobalLookup)
STORE_IP();
acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
STORE_IP();
acc = Runtime::method_callQmlContextPropertyLookup(engine, index, stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
STORE_IP();
acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallWithSpread)
MOTH_BEGIN_INSTR(TailCall)
@@ -1004,23 +1101,25 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_END_INSTR(Jump)
MOTH_BEGIN_INSTR(JumpTrue)
- if (Q_LIKELY(ACC.integerCompatible())) {
- if (ACC.int_32())
- code += offset;
- } else {
- if (ACC.toBoolean())
- code += offset;
- }
+ bool takeJump;
+ if (Q_LIKELY(ACC.integerCompatible()))
+ takeJump = ACC.int_32();
+ else
+ takeJump = ACC.toBoolean();
+ traceJumpTakesTruePath(takeJump, function, traceSlot);
+ if (takeJump)
+ code += offset;
MOTH_END_INSTR(JumpTrue)
MOTH_BEGIN_INSTR(JumpFalse)
- if (Q_LIKELY(ACC.integerCompatible())) {
- if (!ACC.int_32())
- code += offset;
- } else {
- if (!ACC.toBoolean())
- code += offset;
- }
+ bool takeJump;
+ if (Q_LIKELY(ACC.integerCompatible()))
+ takeJump = !ACC.int_32();
+ else
+ takeJump = !ACC.toBoolean();
+ traceJumpTakesTruePath(!takeJump, function, traceSlot);
+ if (takeJump)
+ code += offset;
MOTH_END_INSTR(JumpFalse)
MOTH_BEGIN_INSTR(JumpNoException)
@@ -1189,14 +1288,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
int a = ACC.int_32();
if (a == 0 || a == std::numeric_limits<int>::min()) {
acc = Encode(-static_cast<double>(a));
+ traceDoubleValue(function, traceSlot);
} else {
- acc = sub_int32(0, ACC.int_32());
+ acc = sub_int32(0, ACC.int_32(), function->traceInfo(traceSlot));
}
} else if (ACC.isDouble()) {
acc ^= (1ull << 63); // simply flip sign bit
+ traceDoubleValue(function, traceSlot);
} else {
acc = Encode(-ACC.toNumberImpl());
CHECK_EXCEPTION;
+ traceOtherValue(function, traceSlot);
}
MOTH_END_INSTR(UMinus)
@@ -1207,49 +1309,57 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(Increment)
if (Q_LIKELY(ACC.integerCompatible())) {
- acc = add_int32(ACC.int_32(), 1);
+ acc = add_int32(ACC.int_32(), 1, function->traceInfo(traceSlot));
} else if (ACC.isDouble()) {
acc = QV4::Encode(ACC.doubleValue() + 1.);
+ traceDoubleValue(function, traceSlot);
} else {
acc = Encode(ACC.toNumberImpl() + 1.);
CHECK_EXCEPTION;
+ traceDoubleValue(function, traceSlot);
}
MOTH_END_INSTR(Increment)
MOTH_BEGIN_INSTR(Decrement)
if (Q_LIKELY(ACC.integerCompatible())) {
- acc = sub_int32(ACC.int_32(), 1);
+ acc = sub_int32(ACC.int_32(), 1, function->traceInfo(traceSlot));
} else if (ACC.isDouble()) {
acc = QV4::Encode(ACC.doubleValue() - 1.);
+ traceDoubleValue(function, traceSlot);
} else {
acc = Encode(ACC.toNumberImpl() - 1.);
CHECK_EXCEPTION;
+ traceDoubleValue(function, traceSlot);
}
MOTH_END_INSTR(Decrement)
MOTH_BEGIN_INSTR(Add)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
- acc = add_int32(left.int_32(), ACC.int_32());
+ acc = add_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot));
} else if (left.isNumber() && ACC.isNumber()) {
acc = Encode(left.asDouble() + ACC.asDouble());
+ traceDoubleValue(function, traceSlot);
} else {
STORE_ACC();
acc = Runtime::method_add(engine, left, accumulator);
CHECK_EXCEPTION;
+ traceOtherValue(function, traceSlot);
}
MOTH_END_INSTR(Add)
MOTH_BEGIN_INSTR(Sub)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
- acc = sub_int32(left.int_32(), ACC.int_32());
+ acc = sub_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot));
} else if (left.isNumber() && ACC.isNumber()) {
acc = Encode(left.asDouble() - ACC.asDouble());
+ traceDoubleValue(function, traceSlot);
} else {
STORE_ACC();
acc = Runtime::method_sub(left, accumulator);
CHECK_EXCEPTION;
+ traceOtherValue(function, traceSlot);
}
MOTH_END_INSTR(Sub)
@@ -1266,13 +1376,15 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(Mul)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
- acc = mul_int32(left.int_32(), ACC.int_32());
+ acc = mul_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot));
} else if (left.isNumber() && ACC.isNumber()) {
acc = Encode(left.asDouble() * ACC.asDouble());
+ traceDoubleValue(function, traceSlot);
} else {
STORE_ACC();
acc = Runtime::method_mul(left, accumulator);
CHECK_EXCEPTION;
+ traceOtherValue(function, traceSlot);
}
MOTH_END_INSTR(Mul)
@@ -1286,6 +1398,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
STORE_ACC();
acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator);
CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
MOTH_END_INSTR(Mod)
MOTH_BEGIN_INSTR(BitAnd)