diff options
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/jsruntime.pri | 20 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4dateobject.cpp | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 41 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executableallocator.cpp | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executableallocator_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function.cpp | 67 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function_p.h | 52 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functiontable_noop.cpp | 65 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functiontable_p.h | 75 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functiontable_unix.cpp | 99 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functiontable_win64.cpp | 153 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4global_p.h | 14 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4math_p.h | 27 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 50 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4variantobject.cpp | 13 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 153 |
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> ¶meters) @@ -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> ¶meters); - 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) |