Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:critical reason:enables-debug-framework
++
++ #include "qqmldebug.h"
++ #include "qqmldebugconnector_p.h"
++ #include "qqmldebugserviceinterfaces_p.h"
++
++ #include <private/qqmlengine_p.h>
++ #include <private/qv4compileddata_p.h>
++
++ #include <QByteArray>
++
++ #include <atomic>
++ #include <cstdio>
++
++ #ifdef Q_OS_WIN
++ #include <qt_windows.h>
++ #endif
++
++ QT_REQUIRE_CONFIG(qml_debug);
++
++ QT_BEGIN_NAMESPACE
++
++ #if __cplusplus >= 202002L
++ # define Q_ATOMIC_FLAG_INIT {}
++ #else
++ # define Q_ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT // deprecated in C++20
++ #endif
++
++ /*!
++ \class QQmlDebuggingEnabler
++ \inmodule QtQml
++ \brief The QQmlDebuggingEnabler class provides methods to enable debugging or profiling.
++
++ Usually QML debugging and profiling is enabled by passing
++ \c{QT_ENABLE_QML_DEBUG} via CMake or \c{CONFIG+=qml_debug} via qmake when
++ building your application. At run time, the application generally parses
++ any \c{-qmljsdebugger} command line arguments to actually start debugging
++ or profiling.
++
++ You can instead handle these tasks manually by using the methods in this
++ class.
++ */
++
++ Q_CONSTINIT static std::atomic_flag s_printedWarning = Q_ATOMIC_FLAG_INIT;
++
++ /*!
++ Enable debugging or profiling. If \a printWarning is \c{true}, print the
++ following warning to stderr:
++
++ \badcode
++ QML debugging is enabled. Only use this in a safe environment.
++ \endcode
++
++ This method is automatically called at startup if \c{QT_ENABLE_QML_DEBUG}
++ or \c{CONFIG+=qml_debug} is passed at build time.
++
++ This method needs to be called one way or another before starting a debug
++ connector of any kind. Otherwise the connector will refuse to start.
++
++ \sa startTcpDebugServer(), connectToLocalDebugger(), startDebugConnector()
++ */
++ void QQmlDebuggingEnabler::enableDebugging(bool printWarning)
++ {
++ if (printWarning && !s_printedWarning.test_and_set(std::memory_order_relaxed)) {
++ QByteArray warning("QML debugging is enabled. Only use this in a safe environment.");
++ if (qEnvironmentVariableIsSet("CLICOLOR_FORCE") || qEnvironmentVariableIsSet("QTC_RUN")
++ || qEnvironmentVariableIsSet("VSCODE_CLI")) {
++ warning = QByteArray("\033[38;2;255;0;0m") + warning + "\033[0m";
++ }
++ warning += '\n';
++ #ifdef Q_OS_WIN
++ // Debuggers on Windows (except vscode) do not display the stderr output
++ if (!qEnvironmentVariableIsSet("VSCODE_CLI"))
++ OutputDebugStringA(warning);
++ #endif
++
++ fprintf(stderr, "%s", warning.constData());
++ fflush(stderr); // We really want to print this warning, even if stderr is buffered
++ }
++ QQmlEnginePrivate::qml_debugging_enabled.store(true, std::memory_order_relaxed);
++ }
++
++ #if QT_DEPRECATED_SINCE(6, 4)
++ /*!
++ \internal
++ */
++ QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning)
++ {
++ enableDebugging(printWarning);
++ };
++ #endif // QT_DEPRECATED_SINCE(6, 4)
++
++ /*!
++ * Retrieves the plugin keys of the debugger services provided by default. The debugger services
++ * enable a debug client to use a Qml/JavaScript debugger, in order to set breakpoints, pause
++ * execution, evaluate expressions and similar debugging tasks.
++ * \return List of plugin keys of default debugger services.
++ */
++ QStringList QQmlDebuggingEnabler::debuggerServices()
++ {
++ return {QV4DebugService::s_key, QQmlEngineDebugService::s_key, QDebugMessageService::s_key};
++ }
++
++ /*!
++ * Retrieves the plugin keys of the inspector services provided by default. The inspector services
++ * enable a debug client to use a visual inspector tool for Qt Quick.
++ * \return List of plugin keys of default inspector services.
++ */
++ QStringList QQmlDebuggingEnabler::inspectorServices()
++ {
++ return {QQmlInspectorService::s_key};
++ }
++
++ /*!
++ * Retrieves the names of the profiler services provided by default. The profiler services enable a
++ * debug client to use a profiler and track the time taken by various QML and JavaScript constructs,
++ * as well as the QtQuick SceneGraph.
++ * \return List of plugin keys of default profiler services.
++ */
++ QStringList QQmlDebuggingEnabler::profilerServices()
++ {
++ return {QQmlProfilerService::s_key, QQmlEngineControlService::s_key, QDebugMessageService::s_key};
++ }
++
++ /*!
++ * Retrieves the plugin keys of the debug services designed to be used with a native debugger. The
++ * native debugger will communicate with these services by directly reading and writing the
++ * application's memory.
++ * \return List of plugin keys of debug services designed to be used with a native debugger.
++ */
++ QStringList QQmlDebuggingEnabler::nativeDebuggerServices()
++ {
++ return {QQmlNativeDebugService::s_key};
++ }
++
++ /*!
++ * Restricts the services available from the debug connector. The connector will scan plugins in the
++ * "qmltooling" subdirectory of the default plugin path. If this function is not called before the
++ * debug connector is enabled, all services found that way will be available to any client. If this
++ * function is called, only the services with plugin keys given in \a services will be available.
++ *
++ * Use this method to disable debugger and inspector services when profiling to get better
++ * performance and more realistic profiles. The debugger service will put any JavaScript engine it
++ * connects to into interpreted mode, disabling the JIT compiler.
++ *
++ * \sa debuggerServices(), profilerServices(), inspectorServices()
++ */
++ void QQmlDebuggingEnabler::setServices(const QStringList &services)
++ {
++ QQmlDebugConnector::setServices(services);
++ }
++
++ /*!
++ * \enum QQmlDebuggingEnabler::StartMode
++ *
++ * Defines the debug connector's start behavior. You can interrupt QML engines starting while a
++ * debug client is connecting, in order to set breakpoints in or profile startup code.
++ *
++ * \value DoNotWaitForClient Run any QML engines as usual while the debug services are connecting.
++ * \value WaitForClient If a QML engine starts while the debug services are connecting,
++ * interrupt it until they are done.
++ */
++
++ /*!
++ * Enables debugging for QML engines created after calling this function. The debug connector will
++ * listen on \a port at \a hostName and block the QML engine until it receives a connection if
++ * \a mode is \c WaitForClient. If \a mode is not specified it won't block and if \a hostName is not
++ * specified it will listen on all available interfaces. You can only start one debug connector at a
++ * time. A debug connector may have already been started if the -qmljsdebugger= command line
++ * argument was given. This method returns \c true if a new debug connector was successfully
++ * started, or \c false otherwise.
++ */
++ bool QQmlDebuggingEnabler::startTcpDebugServer(int port, StartMode mode, const QString &hostName)
++ {
++ QVariantHash configuration;
++ configuration[QLatin1String("portFrom")] = configuration[QLatin1String("portTo")] = port;
++ configuration[QLatin1String("block")] = (mode == WaitForClient);
++ configuration[QLatin1String("hostAddress")] = hostName;
++ return startDebugConnector(QLatin1String("QQmlDebugServer"), configuration);
++ }
++
++ /*!
++ * \since 5.6
++ *
++ * Enables debugging for QML engines created after calling this function. The debug connector will
++ * connect to a debugger waiting on a local socket at the given \a socketFileName and block the QML
++ * engine until the connection is established if \a mode is \c WaitForClient. If \a mode is not
++ * specified it will not block. You can only start one debug connector at a time. A debug connector
++ * may have already been started if the -qmljsdebugger= command line argument was given. This method
++ * returns \c true if a new debug connector was successfully started, or \c false otherwise.
++ */
++ bool QQmlDebuggingEnabler::connectToLocalDebugger(const QString &socketFileName, StartMode mode)
++ {
++ QVariantHash configuration;
++ configuration[QLatin1String("fileName")] = socketFileName;
++ configuration[QLatin1String("block")] = (mode == WaitForClient);
++ return startDebugConnector(QLatin1String("QQmlDebugServer"), configuration);
++ }
++
++ /*!
++ * \since 5.7
++ *
++ * Enables debugging for QML engines created after calling this function. A debug connector plugin
++ * specified by \a pluginName will be loaded and started using the given \a configuration. Supported
++ * configuration entries and their semantics depend on the plugin being loaded. You can only start
++ * one debug connector at a time. A debug connector may have already been started if the
++ * -qmljsdebugger= command line argument was given. This method returns \c true if a new debug
++ * connector was successfully started, or \c false otherwise.
++ */
++ bool QQmlDebuggingEnabler::startDebugConnector(const QString &pluginName,
++ const QVariantHash &configuration)
++ {
++ QQmlDebugConnector::setPluginKey(pluginName);
++ QQmlDebugConnector *connector = QQmlDebugConnector::instance();
++ return connector ? connector->open(configuration) : false;
++ }
++
++ enum { HookCount = 4 };
++
++ // Only add to the end, and bump version if you do.
++ quintptr Q_QML_EXPORT qtDeclarativeHookData[] = {
++ // Version of this Array. Bump if you add to end.
++ 2,
++
++ // Number of entries in this array.
++ HookCount,
++
++ // TypeInformationVersion, an integral value, bumped whenever private
++ // object sizes or member offsets that are used in Qt Creator's
++ // data structure "pretty printing" change.
++ 4,
++
++ // Version of the cache data.
++ QV4_DATA_STRUCTURE_VERSION
++ };
++
++ Q_STATIC_ASSERT(HookCount == sizeof(qtDeclarativeHookData) / sizeof(qtDeclarativeHookData[0]));
++
++ QT_END_NAMESPACE