/* * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "executor.h" #include #include "api.h" #include "runtime.h" namespace AddOn { static QMap> apiGroups{ {"C_Debug", { {"debug", &luaApi_Debug_debug}, // (string) -> () }}, {"C_Desktop", { {"desktopEnvironment", &luaApi_Desktop_desktopEnvironment}, // () -> string {"language", &luaApi_Desktop_language}, // () -> string {"qtStyleList", &luaApi_Desktop_qtStyleList}, // () -> [string] {"systemAppMode", &luaApi_Desktop_systemAppMode}, // () -> string {"systemStyle", &luaApi_Desktop_systemStyle}, // () -> string }}, }; static void registerApiGroup(RaiiLuaState &L, const QString &name) { L.push(apiGroups[name]); L.setGlobal(name); } extern "C" void luaHook_timeoutKiller(lua_State *L, lua_Debug *ar [[maybe_unused]]) noexcept { using namespace std::chrono; AddOn::LuaExtraState &extraState = AddOn::RaiiLuaState::extraState(L); auto duration = system_clock::now() - extraState.timeStart; if (duration > extraState.timeLimit) { lua_pushfstring(L, "timeout in script '%s' (%d/%d ms)", extraState.name.toUtf8().constData(), int(duration_cast(duration).count()), int(duration_cast(extraState.timeLimit).count())); lua_error(L); } }; ThemeExecutor::ThemeExecutor() : SimpleExecutor({"C_Debug", "C_Desktop"}) {} QJsonObject ThemeExecutor::operator()(const QByteArray &script, const QString &name) { using namespace std::chrono_literals; QJsonValue result = SimpleExecutor::runScript(script, "theme:" + name, 100ms); if (result.isObject() || result.isNull()) return result.toObject(); else throw LuaError("Theme script must return an object."); } QJsonValue SimpleExecutor::runScript(const QByteArray &script, const QString &name, std::chrono::microseconds timeLimit) { RaiiLuaState L(name, timeLimit); L.openLibs(); for (auto &api : mApis) registerApiGroup(L, api); int retLoad = L.loadBuffer(script, name); if (retLoad != 0) throw LuaError("Lua script load error."); L.setHook(&luaHook_timeoutKiller, LUA_MASKCOUNT, 1'000'000); // ~5ms on early 2020s desktop CPUs L.setTimeStart(); int callResult = L.pCall(0, 1, 0); if (callResult != 0) { throw LuaError(QString("Lua error: %1.").arg(L.popString())); } return L.fetch(1); } } // namespace AddOn