forked from qt-creator/qt-creator
Lua: Add connection guard
Improves safety by limiting connection lifetime to lifetime of lua vm. Change-Id: I508b0814fdda169f5e30d7c3282132e3c622a0a5 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -39,6 +39,8 @@ void addFetchModule()
|
||||
{
|
||||
LuaEngine::registerProvider(
|
||||
"Fetch", [](sol::state_view lua) -> sol::object {
|
||||
const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
|
||||
|
||||
sol::table async = lua.script("return require('async')", "_fetch_").get<sol::table>();
|
||||
sol::function wrap = async["wrap"];
|
||||
|
||||
@@ -58,9 +60,10 @@ void addFetchModule()
|
||||
.arg(r->error());
|
||||
});
|
||||
|
||||
fetch["fetch_cb"] = [](const sol::table &options,
|
||||
const sol::function &callback,
|
||||
const sol::this_state &thisState) {
|
||||
fetch["fetch_cb"] = [guard = pluginSpec->connectionGuard.get()](
|
||||
const sol::table &options,
|
||||
const sol::function &callback,
|
||||
const sol::this_state &thisState) {
|
||||
auto url = options.get<QString>("url");
|
||||
|
||||
auto method = (options.get_or<QString>("method", "GET")).toLower();
|
||||
@@ -85,10 +88,7 @@ void addFetchModule()
|
||||
|
||||
if (convertToTable) {
|
||||
QObject::connect(
|
||||
reply,
|
||||
&QNetworkReply::finished,
|
||||
&LuaEngine::instance(),
|
||||
[reply, thisState, callback]() {
|
||||
reply, &QNetworkReply::finished, guard, [reply, thisState, callback]() {
|
||||
reply->deleteLater();
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
@@ -120,7 +120,7 @@ void addFetchModule()
|
||||
|
||||
} else {
|
||||
QObject::connect(
|
||||
reply, &QNetworkReply::finished, &LuaEngine::instance(), [reply, callback]() {
|
||||
reply, &QNetworkReply::finished, guard, [reply, callback]() {
|
||||
// We don't want the network reply to be deleted by the manager, but
|
||||
// by the Lua GC
|
||||
reply->setParent(nullptr);
|
||||
|
||||
@@ -253,12 +253,12 @@ void addInstallModule()
|
||||
sol::function wrap = async["wrap"];
|
||||
|
||||
sol::table install = lua.create_table();
|
||||
const ScriptPluginSpec pluginSpec = lua.get<ScriptPluginSpec>("PluginSpec");
|
||||
const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
|
||||
|
||||
install["packageInfo"] =
|
||||
[pluginSpec](const QString &name, sol::this_state l) -> sol::optional<sol::table> {
|
||||
expected_str<QJsonObject> obj
|
||||
= getInstalledPackageInfo(pluginSpec.appDataPath, name);
|
||||
= getInstalledPackageInfo(pluginSpec->appDataPath, name);
|
||||
if (!obj)
|
||||
throw sol::error(obj.error().toStdString());
|
||||
|
||||
@@ -302,7 +302,7 @@ void addInstallModule()
|
||||
}
|
||||
|
||||
const Utils::Id infoBarId = Utils::Id::fromString(
|
||||
"Install" + pluginSpec.name + QString::number(qHash(installOptionsList)));
|
||||
"Install" + pluginSpec->name + QString::number(qHash(installOptionsList)));
|
||||
|
||||
InfoBarEntry entry(infoBarId, msg, InfoBarEntry::GlobalSuppression::Enabled);
|
||||
|
||||
@@ -315,7 +315,7 @@ void addInstallModule()
|
||||
progress->setDisplayName(Tr::tr("Installing package(s) %1").arg("..."));
|
||||
|
||||
tree->setRecipe(
|
||||
installRecipe(pluginSpec.appDataPath, installOptionsList, callback));
|
||||
installRecipe(pluginSpec->appDataPath, installOptionsList, callback));
|
||||
tree->start();
|
||||
|
||||
Core::ICore::infoBar()->removeInfo(infoBarId);
|
||||
@@ -327,7 +327,7 @@ void addInstallModule()
|
||||
const QString markdown
|
||||
= Tr::tr("The plugin \"**%1**\" would like to install the following "
|
||||
"package(s):\n\n")
|
||||
.arg(pluginSpec.name)
|
||||
.arg(pluginSpec->name)
|
||||
+ Utils::transform(installOptionsList, [](const InstallOptions &options) {
|
||||
return QString("* %1 - %2 (from: [%3](%3))")
|
||||
.arg(options.name, options.version, options.url.toString());
|
||||
|
||||
@@ -89,7 +89,7 @@ HAS_MEM_FUNC(setTitle, hasSetTitle);
|
||||
HAS_MEM_FUNC(setValue, hasSetValue);
|
||||
|
||||
template<class T>
|
||||
void setProperties(std::unique_ptr<T> &item, const sol::table &children)
|
||||
void setProperties(std::unique_ptr<T> &item, const sol::table &children, QObject *guard)
|
||||
{
|
||||
if constexpr (hasOnTextChanged<T, void (T::*)(const QString &)>::value) {
|
||||
sol::optional<sol::protected_function> onTextChanged
|
||||
@@ -100,7 +100,7 @@ void setProperties(std::unique_ptr<T> &item, const sol::table &children)
|
||||
auto res = LuaEngine::void_safe_call(f, text);
|
||||
QTC_CHECK_EXPECTED(res);
|
||||
},
|
||||
&LuaEngine::instance());
|
||||
guard);
|
||||
}
|
||||
}
|
||||
if constexpr (hasOnClicked<T, void (T::*)(const std::function<void()> &, QObject *guard)>::value) {
|
||||
@@ -112,7 +112,7 @@ void setProperties(std::unique_ptr<T> &item, const sol::table &children)
|
||||
auto res = LuaEngine::void_safe_call(f);
|
||||
QTC_CHECK_EXPECTED(res);
|
||||
},
|
||||
&LuaEngine::instance());
|
||||
guard);
|
||||
}
|
||||
}
|
||||
if constexpr (hasSetText<T, void (T::*)(const QString &)>::value) {
|
||||
@@ -129,11 +129,11 @@ void setProperties(std::unique_ptr<T> &item, const sol::table &children)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::unique_ptr<T> constructWidgetType(const sol::table &children)
|
||||
std::unique_ptr<T> constructWidgetType(const sol::table &children, QObject *guard)
|
||||
{
|
||||
std::unique_ptr<T> item(new T({}));
|
||||
constructWidget(item, children);
|
||||
setProperties(item, children);
|
||||
setProperties(item, children, guard);
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -143,10 +143,10 @@ std::unique_ptr<Tab> constructTab(const QString &tabName, const Layout &layout)
|
||||
return item;
|
||||
}
|
||||
|
||||
std::unique_ptr<TabWidget> constructTabWidget(const sol::table &children)
|
||||
std::unique_ptr<TabWidget> constructTabWidget(const sol::table &children, QObject *guard)
|
||||
{
|
||||
std::unique_ptr<TabWidget> item(new TabWidget({}));
|
||||
setProperties(item, children);
|
||||
setProperties(item, children, guard);
|
||||
for (size_t i = 1; i < children.size(); ++i) {
|
||||
const auto &child = children[i];
|
||||
if (child.is<Tab *>())
|
||||
@@ -177,6 +177,9 @@ std::unique_ptr<Splitter> constructSplitter(const sol::table &children)
|
||||
void addLayoutModule()
|
||||
{
|
||||
LuaEngine::registerProvider("Layout", [](sol::state_view l) -> sol::object {
|
||||
const ScriptPluginSpec *pluginSpec = l.get<ScriptPluginSpec *>("PluginSpec");
|
||||
QObject *guard = pluginSpec->connectionGuard.get();
|
||||
|
||||
sol::table layout = l.create_table();
|
||||
|
||||
layout.new_usertype<Span>(
|
||||
@@ -225,14 +228,18 @@ void addLayoutModule()
|
||||
layout.new_usertype<PushButton>(
|
||||
"PushButton",
|
||||
sol::call_constructor,
|
||||
sol::factories(&constructWidgetType<PushButton>),
|
||||
sol::factories([guard](const sol::table &children) {
|
||||
return constructWidgetType<PushButton>(children, guard);
|
||||
}),
|
||||
sol::base_classes,
|
||||
sol::bases<Widget, Object, Thing>());
|
||||
|
||||
layout.new_usertype<Widget>(
|
||||
"Widget",
|
||||
sol::call_constructor,
|
||||
sol::factories(&constructWidgetType<Widget>),
|
||||
sol::factories([guard](const sol::table &children) {
|
||||
return constructWidgetType<Widget>(children, guard);
|
||||
}),
|
||||
"show",
|
||||
&Widget::show,
|
||||
"resize",
|
||||
@@ -243,7 +250,9 @@ void addLayoutModule()
|
||||
layout.new_usertype<Stack>(
|
||||
"Stack",
|
||||
sol::call_constructor,
|
||||
sol::factories(&constructWidgetType<Stack>),
|
||||
sol::factories([guard](const sol::table &children) {
|
||||
return constructWidgetType<Stack>(children, guard);
|
||||
}),
|
||||
sol::base_classes,
|
||||
sol::bases<Widget, Object, Thing>());
|
||||
|
||||
@@ -257,14 +266,18 @@ void addLayoutModule()
|
||||
layout.new_usertype<TextEdit>(
|
||||
"TextEdit",
|
||||
sol::call_constructor,
|
||||
sol::factories(&constructWidgetType<TextEdit>),
|
||||
sol::factories([guard](const sol::table &children) {
|
||||
return constructWidgetType<TextEdit>(children, guard);
|
||||
}),
|
||||
sol::base_classes,
|
||||
sol::bases<Widget, Object, Thing>());
|
||||
|
||||
layout.new_usertype<SpinBox>(
|
||||
"SpinBox",
|
||||
sol::call_constructor,
|
||||
sol::factories(&constructWidgetType<SpinBox>),
|
||||
sol::factories([guard](const sol::table &children) {
|
||||
return constructWidgetType<SpinBox>(children, guard);
|
||||
}),
|
||||
sol::base_classes,
|
||||
sol::bases<Widget, Object, Thing>());
|
||||
layout.new_usertype<Splitter>(
|
||||
@@ -276,20 +289,26 @@ void addLayoutModule()
|
||||
layout.new_usertype<ToolBar>(
|
||||
"ToolBar",
|
||||
sol::call_constructor,
|
||||
sol::factories(&constructWidgetType<ToolBar>),
|
||||
sol::factories([guard](const sol::table &children) {
|
||||
return constructWidgetType<ToolBar>(children, guard);
|
||||
}),
|
||||
sol::base_classes,
|
||||
sol::bases<Widget, Object, Thing>());
|
||||
layout.new_usertype<TabWidget>(
|
||||
"TabWidget",
|
||||
sol::call_constructor,
|
||||
sol::factories(&constructTabWidget),
|
||||
sol::factories([guard](const sol::table &children) {
|
||||
return constructTabWidget(children, guard);
|
||||
}),
|
||||
sol::base_classes,
|
||||
sol::bases<Widget, Object, Thing>());
|
||||
|
||||
layout.new_usertype<Group>(
|
||||
"Group",
|
||||
sol::call_constructor,
|
||||
sol::factories(&constructWidgetType<Group>),
|
||||
sol::factories([guard](const sol::table &children) {
|
||||
return constructWidgetType<Group>(children, guard);
|
||||
}),
|
||||
sol::base_classes,
|
||||
sol::bases<Widget, Object, Thing>());
|
||||
|
||||
@@ -300,22 +319,6 @@ void addLayoutModule()
|
||||
layout["noMargin"] = &noMargin;
|
||||
layout["normalMargin"] = &normalMargin;
|
||||
|
||||
//layout["customMargin"] = [](int left, int top, int right, int bottom) {
|
||||
// return customMargin(QMargins(left, top, right, bottom));
|
||||
//};
|
||||
// layout["withFormAlignment"] = &withFormAlignment;
|
||||
// layout["columnStretch"] = &columnStretch;
|
||||
// layout["spacing"] = &spacing;
|
||||
// layout["windowTitle"] = &windowTitle;
|
||||
// layout["fieldGrowthPolicy"] = &fieldGrowthPolicy;
|
||||
// layout["id"] = &id;
|
||||
layout["onTextChanged"] = [](const sol::function &f) {
|
||||
return onTextChanged([f](const QString &text) {
|
||||
auto res = LuaEngine::void_safe_call(f, text);
|
||||
QTC_CHECK_EXPECTED(res);
|
||||
});
|
||||
};
|
||||
|
||||
return layout;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,30 +12,31 @@ namespace Lua::Internal {
|
||||
|
||||
void addProcessModule()
|
||||
{
|
||||
LuaEngine::registerProvider(
|
||||
"Process", [](sol::state_view lua) -> sol::object {
|
||||
sol::table async = lua.script("return require('async')", "_process_").get<sol::table>();
|
||||
sol::function wrap = async["wrap"];
|
||||
LuaEngine::registerProvider("Process", [](sol::state_view lua) -> sol::object {
|
||||
const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
|
||||
|
||||
sol::table process = lua.create_table();
|
||||
sol::table async = lua.script("return require('async')", "_process_").get<sol::table>();
|
||||
sol::function wrap = async["wrap"];
|
||||
|
||||
process["runInTerminal_cb"] = [](const QString &cmdline, const sol::function &cb) {
|
||||
sol::table process = lua.create_table();
|
||||
|
||||
process["runInTerminal_cb"] =
|
||||
[guard
|
||||
= pluginSpec->connectionGuard.get()](const QString &cmdline, const sol::function &cb) {
|
||||
Process *p = new Process;
|
||||
p->setTerminalMode(TerminalMode::Run);
|
||||
p->setCommand(CommandLine::fromUserInput((cmdline)));
|
||||
p->setEnvironment(Environment::systemEnvironment());
|
||||
|
||||
QObject::connect(p, &Process::done, &LuaEngine::instance(), [p, cb]() {
|
||||
cb(p->exitCode());
|
||||
});
|
||||
QObject::connect(p, &Process::done, guard, [p, cb]() { cb(p->exitCode()); });
|
||||
|
||||
p->start();
|
||||
};
|
||||
|
||||
process["runInTerminal"] = wrap(process["runInTerminal_cb"]);
|
||||
process["runInTerminal"] = wrap(process["runInTerminal_cb"]);
|
||||
|
||||
return process;
|
||||
});
|
||||
return process;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Lua::Internal
|
||||
|
||||
@@ -19,16 +19,19 @@ void addUtilsModule()
|
||||
LuaEngine::registerProvider(
|
||||
"Utils",
|
||||
[futureSync = Utils::FutureSynchronizer()](sol::state_view lua) mutable -> sol::object {
|
||||
const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
|
||||
|
||||
auto async = lua.script("return require('async')", "_utils_").get<sol::table>();
|
||||
|
||||
sol::table utils = lua.create_table();
|
||||
|
||||
utils.set_function("waitms_cb", [](int ms, const sol::function &cb) {
|
||||
QTimer::singleShot(ms, &LuaEngine::instance(), [cb]() { cb(); });
|
||||
utils.set_function("waitms_cb", [guard = pluginSpec->connectionGuard.get()](int ms, const sol::function &cb) {
|
||||
QTimer::singleShot(ms, guard, [cb]() { cb(); });
|
||||
});
|
||||
|
||||
auto dirEntries_cb =
|
||||
[&futureSync](const FilePath &p, const sol::table &options, const sol::function &cb) {
|
||||
[&futureSync, guard = pluginSpec->connectionGuard.get()](
|
||||
const FilePath &p, const sol::table &options, const sol::function &cb) {
|
||||
const QStringList nameFilters = options.get_or<QStringList>("nameFilters", {});
|
||||
QDir::Filters fileFilters
|
||||
= (QDir::Filters) options.get_or<int>("fileFilters", QDir::NoFilter);
|
||||
@@ -53,22 +56,22 @@ void addUtilsModule()
|
||||
|
||||
futureSync.addFuture<FilePath>(future);
|
||||
|
||||
Utils::onFinished<FilePath>(
|
||||
future, &LuaEngine::instance(), [cb](const QFuture<FilePath> &future) {
|
||||
cb(future.results());
|
||||
});
|
||||
Utils::onFinished<FilePath>(future, guard, [cb](const QFuture<FilePath> &future) {
|
||||
cb(future.results());
|
||||
});
|
||||
};
|
||||
|
||||
auto searchInPath_cb = [&futureSync](const FilePath &p, const sol::function &cb) {
|
||||
auto searchInPath_cb = [&futureSync,
|
||||
guard = pluginSpec->connectionGuard
|
||||
.get()](const FilePath &p, const sol::function &cb) {
|
||||
QFuture<FilePath> future = Utils::asyncRun(
|
||||
[p](QPromise<FilePath> &promise) { promise.addResult(p.searchInPath()); });
|
||||
|
||||
futureSync.addFuture<FilePath>(future);
|
||||
|
||||
Utils::onFinished<FilePath>(
|
||||
future, &LuaEngine::instance(), [cb](const QFuture<FilePath> &future) {
|
||||
cb(future.result());
|
||||
});
|
||||
Utils::onFinished<FilePath>(future, guard, [cb](const QFuture<FilePath> &future) {
|
||||
cb(future.result());
|
||||
});
|
||||
};
|
||||
|
||||
utils.set_function("__dirEntries_cb__", dirEntries_cb);
|
||||
|
||||
@@ -156,14 +156,13 @@ expected_str<void> LuaEngine::prepareSetup(
|
||||
const FilePath appDataPath = Core::ICore::userResourcePath() / "plugin-data" / "lua"
|
||||
/ pluginSpec.location().fileName();
|
||||
|
||||
sol::environment env(lua, sol::create, lua.globals());
|
||||
|
||||
lua.new_usertype<ScriptPluginSpec>(
|
||||
"PluginSpec", sol::no_constructor, "name", sol::property([](ScriptPluginSpec &self) {
|
||||
return self.name;
|
||||
}));
|
||||
|
||||
lua["PluginSpec"] = ScriptPluginSpec{pluginSpec.name(), appDataPath};
|
||||
lua["PluginSpec"]
|
||||
= ScriptPluginSpec{pluginSpec.name(), appDataPath, std::make_unique<QObject>()};
|
||||
|
||||
// TODO: only register what the plugin requested
|
||||
for (const auto &[name, func] : d->m_providers.asKeyValueRange()) {
|
||||
|
||||
@@ -37,6 +37,7 @@ struct ScriptPluginSpec
|
||||
{
|
||||
QString name;
|
||||
Utils::FilePath appDataPath;
|
||||
std::unique_ptr<QObject> connectionGuard;
|
||||
};
|
||||
|
||||
class LUA_EXPORT LuaEngine final : public QObject
|
||||
|
||||
Reference in New Issue
Block a user