diff --git a/src/plasma/pluginloader.cpp b/src/plasma/pluginloader.cpp --- a/src/plasma/pluginloader.cpp +++ b/src/plasma/pluginloader.cpp @@ -69,15 +69,23 @@ static QString s_servicesPluginDir; static QString s_containmentActionsPluginDir; - // We only use this cache during start of the process to speed up many consecutive calls - // After that, we're too afraid to produce race conditions and it's not that time-critical anyway - // the 20 seconds here means that the cache is only used within 20sec during startup, after that, - // complexity goes up and we'd have to update the cache in order to avoid subtle bugs - // just not using the cache is way easier then, since it doesn't make *that* much of a difference, - // anyway - int maxCacheAge = 20; - qint64 pluginCacheAge = 0; - QHash> pluginCache; + class Cache { + // We only use this cache during start of the process to speed up many consecutive calls + // After that, we're too afraid to produce race conditions and it's not that time-critical anyway + // the 20 seconds here means that the cache is only used within 20sec during startup, after that, + // complexity goes up and we'd have to update the cache in order to avoid subtle bugs + // just not using the cache is way easier then, since it doesn't make *that* much of a difference, + // anyway + int maxCacheAge = 20; + qint64 pluginCacheAge = 0; + QHash> plugins; + + public: + QVector findPluginsById(const QString& name, const QStringList &dirs); + }; + Cache plasmoidCache; + Cache dataengineCache; + Cache containmentactionCache; }; QSet PluginLoaderPrivate::s_customCategories; @@ -171,49 +179,8 @@ appletId = ++AppletPrivate::s_maxAppletId; } - const qint64 now = qRound64(QDateTime::currentMSecsSinceEpoch() / 1000.0); - bool useRuntimeCache = true; - if (now - d->pluginCacheAge > d->maxCacheAge && d->pluginCacheAge != 0) { - // cache is old and we're not within a few seconds of startup anymore - useRuntimeCache = false; - d->pluginCache.clear(); - } - - if (d->pluginCacheAge == 0) { - // Find all the plugins now, but only once - d->pluginCacheAge = now; - - auto insertIntoCache = [this](const QString &pluginPath) { - KPluginMetaData metadata(pluginPath); - if (!metadata.isValid()) { - return; - } - - d->pluginCache[metadata.pluginId()].append(metadata); - }; - - KPluginLoader::forEachPlugin(PluginLoaderPrivate::s_plasmoidsPluginDir, insertIntoCache); - // COMPAT CODE for applets installed into the toplevel plugins dir by mistake. - KPluginLoader::forEachPlugin(QString(), insertIntoCache); - } - - //if name wasn't a path, pluginName == name - const QString pluginName = name.section(QLatin1Char('/'), -1); - - QVector plugins; - - if (useRuntimeCache) { - auto it = d->pluginCache.constFind(pluginName); - if (it != d->pluginCache.constEnd()) { - plugins = *it; - } - } else { - plugins = KPluginLoader::findPluginsById(PluginLoaderPrivate::s_plasmoidsPluginDir, pluginName); - // COMPAT CODE for applets installed into the toplevel plugins dir by mistake. - if (plugins.isEmpty()) { - plugins = KPluginLoader::findPluginsById(QString(), pluginName); - } - } + // Need to pass the empty directory because it's where plasmoids used to be + const auto plugins = d->plasmoidCache.findPluginsById(name, { PluginLoaderPrivate::s_plasmoidsPluginDir, {} }); const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), name); @@ -263,19 +230,12 @@ } // Look for C++ plugins first - auto filter = [&name](const KPluginMetaData &md) -> bool - { - return md.pluginId() == name; - }; - QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter); - + const QVector plugins = d->dataengineCache.findPluginsById(name, {PluginLoaderPrivate::s_dataEnginePluginDir}); if (!plugins.isEmpty()) { - KPluginLoader loader(plugins.first().fileName()); + KPluginLoader loader(plugins.constFirst().fileName()); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); - if (factory) { - engine = factory->create(nullptr, argsWithMetaData); - } + return factory ? factory->create(nullptr, argsWithMetaData) : nullptr; } if (engine) { return engine; @@ -406,13 +366,7 @@ return actions; } - - // Look for C++ plugins first - auto filter = [&name](const KPluginMetaData &md) -> bool - { - return md.pluginId() == name; - }; - QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_containmentActionsPluginDir, filter); + const QVector plugins = d->containmentactionCache.findPluginsById(name, {PluginLoaderPrivate::s_containmentActionsPluginDir}); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); @@ -891,5 +845,53 @@ return true; } -} // Plasma Namespace +QVector PluginLoaderPrivate::Cache::findPluginsById(const QString& name, const QStringList &dirs) +{ + const qint64 now = qRound64(QDateTime::currentMSecsSinceEpoch() / 1000.0); + bool useRuntimeCache = true; + if (pluginCacheAge == 0) { + // Find all the plugins now, but only once + pluginCacheAge = now; + + auto insertIntoCache = [this](const QString &pluginPath) { + KPluginMetaData metadata(pluginPath); + if (!metadata.isValid()) { + qWarning() << "invalid metadata" << pluginPath; + return; + } + + plugins[metadata.pluginId()].append(metadata); + }; + + for (const QString &dir : dirs) + KPluginLoader::forEachPlugin(dir, insertIntoCache); + } else if (now - pluginCacheAge > maxCacheAge) { + // cache is old and we're not within a few seconds of startup anymore + useRuntimeCache = false; + plugins.clear(); + } + + //if name wasn't a path, pluginName == name + const QString pluginName = name.section(QLatin1Char('/'), -1); + + QVector ret; + + if (useRuntimeCache) { + auto it = plugins.constFind(pluginName); + if (it != plugins.constEnd()) { + ret = *it; + } + qCDebug(LOG_PLASMA) << "loading applet by name" << name << useRuntimeCache << ret.size(); + } else { + + for (const auto& dir : dirs) { + ret = KPluginLoader::findPluginsById(dir, pluginName); + if (!ret.isEmpty()) + break; + } + } + return ret; +} + +} // Plasma Namespace diff --git a/src/plasma/scripting/scriptengine.cpp b/src/plasma/scripting/scriptengine.cpp --- a/src/plasma/scripting/scriptengine.cpp +++ b/src/plasma/scripting/scriptengine.cpp @@ -20,7 +20,7 @@ #include "scripting/scriptengine.h" #include -#include +#include #include "applet.h" #include "dataengine.h" @@ -32,6 +32,23 @@ namespace Plasma { +static QVector listEngines(Types::ComponentTypes types, std::function filter) +{ + QVector ret; + const QVector plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines")); + ret.reserve(plugins.size()); + for (const auto &plugin : plugins) { + if (!filter(plugin)) + continue; + const QStringList componentTypes = KPluginMetaData::readStringList(plugins.first().rawData(), QStringLiteral("X-Plasma-ComponentTypes")); + if (((types & Types::AppletComponent) && componentTypes.contains(QStringLiteral("Applet"))) + ||((types & Types::DataEngineComponent) && componentTypes.contains(QStringLiteral("DataEngine")))) { + ret << plugin; + } + } + return ret; +} + ScriptEngine::ScriptEngine(QObject *parent) : QObject(parent), d(nullptr) @@ -61,44 +78,42 @@ QStringList knownLanguages(Types::ComponentTypes types) { QStringList languages; - const QVector plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines")); + const QVector plugins = listEngines(types, [] (const KPluginMetaData &) -> bool { return true;}); - foreach (const auto &plugin, plugins) { - const QStringList componentTypes = KPluginMetaData::readStringList(plugins.first().rawData(), QStringLiteral("X-Plasma-ComponentTypes")); - if (((types & Types::AppletComponent) && componentTypes.contains(QStringLiteral("Applet"))) - ||((types & Types::DataEngineComponent) && componentTypes.contains(QStringLiteral("DataEngine")))) { - languages << plugin.value(QStringLiteral("X-Plasma-API")); - } - } + for (const auto &plugin : plugins) + languages << plugin.value(QStringLiteral("X-Plasma-API")); return languages; } +typedef QHash> EngineCache; +Q_GLOBAL_STATIC(EngineCache, engines) + ScriptEngine *loadEngine(const QString &language, Types::ComponentType type, QObject *parent, const QVariantList &args = QVariantList()) { Q_UNUSED(parent); - ScriptEngine *engine = nullptr; + { + auto it = engines->constFind(language); + if (it != engines->constEnd()) { + return (*it)->factory()->create(nullptr, args); + } + } + ScriptEngine *engine = nullptr; auto filter = [&language](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-Plasma-API")) == language; }; - QVector plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines"), filter); + const QVector plugins = listEngines(type, filter); if (!plugins.isEmpty()) { - const QStringList componentTypes = KPluginMetaData::readStringList(plugins.first().rawData(), QStringLiteral("X-Plasma-ComponentTypes")); - if (((type & Types::AppletComponent) && !componentTypes.contains(QStringLiteral("Applet"))) - || ((type & Types::DataEngineComponent) && !componentTypes.contains(QStringLiteral("DataEngine")))) { - - qCWarning(LOG_PLASMA) << "ScriptEngine" << plugins.first().name() << "does not provide Applet or DataEngine components, returning empty."; - return nullptr; - } - KPluginLoader loader(plugins.first().fileName()); - KPluginFactory *factory = loader.factory(); + QSharedPointer loader(new KPluginLoader(plugins.first().fileName())); + KPluginFactory *factory = loader->factory(); if (factory) { engine = factory->create(nullptr, args); + engines->insert(language, loader); } else { qCWarning(LOG_PLASMA) << "Unable to load" << plugins.first().name() << "ScriptEngine"; }