From 40274e4bf4347f7cba50627cda55c712051e9cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sat, 9 Feb 2019 17:21:31 +0100 Subject: [PATCH] Kill CutterPythonPlugin and use Bindings instead Simplify CutterPlugin Interface Pull Python Plugin Metadata from class vars Also kill cutter_plugin.py Fix Python Plugin Module Loading --- src/CMakeLists.txt | 2 + src/Cutter.pro | 2 - src/bindings/bindings.h | 1 + src/bindings/bindings.xml | 94 +++++++++++++++ src/common/PythonManager.cpp | 4 - src/common/PythonManager.h | 4 - src/dialogs/R2PluginsDialog.cpp | 8 +- src/plugins/CutterPlugin.h | 13 +- src/plugins/CutterPythonPlugin.cpp | 112 ------------------ src/plugins/CutterPythonPlugin.h | 21 ---- src/plugins/PluginManager.cpp | 47 +++++--- src/plugins/sample-cpp/CutterSamplePlugin.cpp | 7 +- src/plugins/sample-cpp/CutterSamplePlugin.h | 7 +- src/plugins/sample-python/sample_python.py | 26 ++-- src/python/cutter.py | 8 +- src/python/cutter_plugin.py | 18 --- src/resources.qrc | 1 - 17 files changed, 168 insertions(+), 207 deletions(-) delete mode 100644 src/plugins/CutterPythonPlugin.cpp delete mode 100644 src/plugins/CutterPythonPlugin.h delete mode 100644 src/python/cutter_plugin.py diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 480d9305..6291dc3d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,6 +123,7 @@ set(BINDINGS_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/cuttercore_wrapper.cpp" "${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/configuration_wrapper.cpp" "${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/cutterdockwidget_wrapper.cpp" + "${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/cutterplugin_wrapper.cpp" "${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/mainwindow_wrapper.cpp") set_property(SOURCE ${BINDINGS_SOURCE} PROPERTY SKIP_AUTOGEN ON) @@ -130,6 +131,7 @@ set_property(SOURCE ${BINDINGS_SOURCE} PROPERTY SKIP_AUTOGEN ON) include_directories("${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/widgets") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/common") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/plugins") add_custom_command(OUTPUT ${BINDINGS_SOURCE} COMMAND shiboken2 --project-file="${CMAKE_CURRENT_BINARY_DIR}/bindings/bindings.txt" diff --git a/src/Cutter.pro b/src/Cutter.pro index 5b153665..4d9f47bf 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -229,7 +229,6 @@ SOURCES += \ dialogs/EditMethodDialog.cpp \ dialogs/LoadNewTypesDialog.cpp \ widgets/SdbWidget.cpp \ - plugins/CutterPythonPlugin.cpp \ common/PythonManager.cpp \ plugins/PluginManager.cpp @@ -343,7 +342,6 @@ HEADERS += \ dialogs/EditMethodDialog.h \ dialogs/LoadNewTypesDialog.h \ widgets/SdbWidget.h \ - plugins/CutterPythonPlugin.h \ common/PythonManager.h \ plugins/PluginManager.h diff --git a/src/bindings/bindings.h b/src/bindings/bindings.h index 181050be..103d3977 100644 --- a/src/bindings/bindings.h +++ b/src/bindings/bindings.h @@ -6,5 +6,6 @@ #include "../common/Configuration.h" #include "../MainWindow.h" #include "../widgets/CutterDockWidget.h" +#include "../plugins/CutterPlugin.h" #endif //CUTTER_BINDINGS_H diff --git a/src/bindings/bindings.xml b/src/bindings/bindings.xml index e6b95808..cbd8fe44 100644 --- a/src/bindings/bindings.xml +++ b/src/bindings/bindings.xml @@ -8,4 +8,98 @@ + + + + SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this); + PyObject *classObject = PyObject_GetAttrString(reinterpret_cast<PyObject *>(wrapper), "__class__"); + if (!classObject) { + PyErr_Print(); + return QString(); + } + PyObject *pyResult = PyObject_GetAttrString(classObject, "name"); + if (!pyResult) { + PyErr_Print(); + return QString(); + } + PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult); + if (!pythonToCpp) { + Shiboken::warning(PyExc_RuntimeWarning, 2, "Invalid return value in function %s, expected %s, got %s.", "CutterPlugin.getName", "QString", Py_TYPE(pyResult)->tp_name); + return ::QString(); + } + QString cppResult; + pythonToCpp(pyResult, &cppResult); + return cppResult; + + + + + SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this); + PyObject *classObject = PyObject_GetAttrString(reinterpret_cast<PyObject *>(wrapper), "__class__"); + if (!classObject) { + PyErr_Print(); + return QString(); + } + PyObject *pyResult = PyObject_GetAttrString(classObject, "author"); + if (!pyResult) { + PyErr_Print(); + return QString(); + } + PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult); + if (!pythonToCpp) { + Shiboken::warning(PyExc_RuntimeWarning, 2, "Invalid return value in function %s, expected %s, got %s.", "CutterPlugin.getAuthor", "QString", Py_TYPE(pyResult)->tp_name); + return ::QString(); + } + QString cppResult; + pythonToCpp(pyResult, &cppResult); + return cppResult; + + + + + SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this); + PyObject *classObject = PyObject_GetAttrString(reinterpret_cast<PyObject *>(wrapper), "__class__"); + if (!classObject) { + PyErr_Print(); + return QString(); + } + PyObject *pyResult = PyObject_GetAttrString(classObject, "description"); + if (!pyResult) { + PyErr_Print(); + return QString(); + } + PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult); + if (!pythonToCpp) { + Shiboken::warning(PyExc_RuntimeWarning, 2, "Invalid return value in function %s, expected %s, got %s.", "CutterPlugin.getDescription", "QString", Py_TYPE(pyResult)->tp_name); + return ::QString(); + } + QString cppResult; + pythonToCpp(pyResult, &cppResult); + return cppResult; + + + + + SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this); + PyObject *classObject = PyObject_GetAttrString(reinterpret_cast<PyObject *>(wrapper), "__class__"); + if (!classObject) { + PyErr_Print(); + return QString(); + } + PyObject *pyResult = PyObject_GetAttrString(classObject, "version"); + if (!pyResult) { + PyErr_Print(); + return QString(); + } + PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult); + if (!pythonToCpp) { + Shiboken::warning(PyExc_RuntimeWarning, 2, "Invalid return value in function %s, expected %s, got %s.", "CutterPlugin.getVersion", "QString", Py_TYPE(pyResult)->tp_name); + return ::QString(); + } + QString cppResult; + pythonToCpp(pyResult, &cppResult); + return cppResult; + + + \ No newline at end of file diff --git a/src/common/PythonManager.cpp b/src/common/PythonManager.cpp index f2029412..0f298abd 100644 --- a/src/common/PythonManager.cpp +++ b/src/common/PythonManager.cpp @@ -7,7 +7,6 @@ #include #include "QtResImporter.h" -#include "plugins/CutterPythonPlugin.h" static PythonManager *uniqueInstance = nullptr; @@ -71,9 +70,6 @@ void PythonManager::initialize() RegQtResImporter(); - // Import other modules - cutterPluginModule = QtResImport("cutter_plugin"); - saveThread(); } diff --git a/src/common/PythonManager.h b/src/common/PythonManager.h index 265c775e..dc94805a 100644 --- a/src/common/PythonManager.h +++ b/src/common/PythonManager.h @@ -28,8 +28,6 @@ public: void restoreThread(); void saveThread(); - PyObject *getCutterPluginModule() { return cutterPluginModule; } - /*! * \brief RAII Helper class to call restoreThread() and saveThread() automatically * @@ -50,8 +48,6 @@ private: wchar_t *pythonHome = nullptr; PyThreadState *pyThreadState = nullptr; int pyThreadStateCounter = 0; - - PyObject *cutterPluginModule; }; #define Python() (PythonManager::getInstance()) diff --git a/src/dialogs/R2PluginsDialog.cpp b/src/dialogs/R2PluginsDialog.cpp index 93812e6b..4328ea07 100644 --- a/src/dialogs/R2PluginsDialog.cpp +++ b/src/dialogs/R2PluginsDialog.cpp @@ -54,10 +54,10 @@ R2PluginsDialog::R2PluginsDialog(QWidget *parent) : for (CutterPlugin *plugin : Plugins()->getPlugins()) { QTreeWidgetItem *item = new QTreeWidgetItem(); - item->setText(0, plugin->name); - item->setText(1, plugin->description); - item->setText(2, plugin->version); - item->setText(3, plugin->author); + item->setText(0, plugin->getName()); + item->setText(1, plugin->getDescription()); + item->setText(2, plugin->getVersion()); + item->setText(3, plugin->getAuthor()); ui->CutterTreeWidget->addTopLevelItem(item); } } diff --git a/src/plugins/CutterPlugin.h b/src/plugins/CutterPlugin.h index 3cb5d354..ed880ba4 100644 --- a/src/plugins/CutterPlugin.h +++ b/src/plugins/CutterPlugin.h @@ -11,16 +11,13 @@ class CutterPlugin { public: virtual ~CutterPlugin() {} - virtual void setupPlugin(CutterCore *core) = 0; + virtual void setupPlugin() = 0; virtual void setupInterface(MainWindow *main) = 0; - QString name; - QString description; - QString version; - QString author; - -protected: - CutterCore *core; + virtual QString getName() const = 0; + virtual QString getAuthor() const = 0; + virtual QString getDescription() const = 0; + virtual QString getVersion() const = 0; }; #define CutterPlugin_iid "org.radare.cutter.plugins.CutterPlugin" diff --git a/src/plugins/CutterPythonPlugin.cpp b/src/plugins/CutterPythonPlugin.cpp deleted file mode 100644 index f066ea76..00000000 --- a/src/plugins/CutterPythonPlugin.cpp +++ /dev/null @@ -1,112 +0,0 @@ - -#include - -#include "CutterPythonPlugin.h" - -CutterPythonPlugin::CutterPythonPlugin(PyObject *pluginModule) -{ - this->pluginModule = pluginModule; - - if (!pluginModule) { - qWarning() << "Could not find plugin module."; - return; - } - pInstance = PyObject_GetAttrString(pluginModule, "plugin"); - if (!pInstance) { - qWarning() << "Cannot find plugin instance."; - return; - } - - auto getStringAttr = [this](const char *attrName) -> QString { - if (!PyObject_HasAttrString(pInstance, attrName)) { - return QString(); - } - PyObject *attr = PyObject_GetAttrString(pInstance, attrName); - if (!attr) { - PyErr_Print(); - return QString(); - } - if (!PyUnicode_Check(attr)) { - return QString(); - } - return QString::fromUtf8(PyUnicode_AsUTF8(attr)); - }; - - name = getStringAttr("name"); - description = getStringAttr("description"); - version = getStringAttr("version"); - author = getStringAttr("author"); -} - -CutterPythonPlugin::~CutterPythonPlugin() -{ - Python()->restoreThread(); - if (pInstance) { - Py_DECREF(pInstance); - } - if (pluginModule) { - Py_DECREF(pluginModule); - } - Python()->saveThread(); -} - -void CutterPythonPlugin::setupPlugin(CutterCore *core) -{ - Q_UNUSED(core) - - Python()->restoreThread(); - // Check setupPlugin method exists - PyObject *setupPlugin = PyObject_GetAttrString(pInstance, "setupPlugin"); - if (!setupPlugin) { - qWarning() << "Cannot find setupPlugin method."; - Python()->saveThread(); - return; - } - Py_DECREF(setupPlugin); - - // Call that method - PyObject *result = PyObject_CallMethod(pInstance, "setupPlugin", nullptr); - if (!result) { - qWarning() << "Error in setupPlugin()."; - PyErr_Print(); - Python()->saveThread(); - } - Py_DECREF(result); - - this->name = getAttributeFromPython("name"); - this->description = getAttributeFromPython("description"); - this->version = getAttributeFromPython("version"); - this->author = getAttributeFromPython("author"); - - Python()->saveThread(); -} - -QString CutterPythonPlugin::getAttributeFromPython(const char *attribute) -{ - QString result; - PyObject *pName = PyObject_GetAttrString(pInstance, attribute); - if (pName) { - result = QString(PyUnicode_AsUTF8(pName)); - } - Py_DECREF(pName); - - return result; -} - -void CutterPythonPlugin::setupInterface(MainWindow *main) -{ - Q_UNUSED(main) - - PyObject *pWidget = nullptr; - Python()->restoreThread(); - pWidget = PyObject_CallMethod(pInstance, "setupInterface", nullptr); - - if (!pWidget) { - qWarning() << "Error in setupInterface()."; - PyErr_Print(); - Python()->saveThread(); - return; - } - - Python()->saveThread(); -} diff --git a/src/plugins/CutterPythonPlugin.h b/src/plugins/CutterPythonPlugin.h deleted file mode 100644 index 34a754a1..00000000 --- a/src/plugins/CutterPythonPlugin.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CUTTERPYTHONPLUGIN_H -#define CUTTERPYTHONPLUGIN_H - -#include "common/PythonManager.h" -#include "CutterPlugin.h" - -class CutterPythonPlugin : public CutterPlugin -{ -public: - CutterPythonPlugin(PyObject* pluginModule); - ~CutterPythonPlugin(); - void setupPlugin(CutterCore *core); - void setupInterface(MainWindow *main); - -private: - PyObject *pluginModule = nullptr; - PyObject *pInstance = nullptr; - QString getAttributeFromPython(const char *attribute); -}; - -#endif // CUTTERPYTHONPLUGIN_H diff --git a/src/plugins/PluginManager.cpp b/src/plugins/PluginManager.cpp index c244921a..58c1fda9 100644 --- a/src/plugins/PluginManager.cpp +++ b/src/plugins/PluginManager.cpp @@ -1,12 +1,12 @@ #ifdef CUTTER_ENABLE_PYTHON #include +#include #endif #include "PluginManager.h" #include "PythonManager.h" #include "CutterPlugin.h" -#include "CutterPythonPlugin.h" #include #include @@ -80,7 +80,7 @@ void PluginManager::loadNativePlugins(const QDir &directory) if (!cutterPlugin) { continue; } - cutterPlugin->setupPlugin(Core()); + cutterPlugin->setupPlugin(); plugins.append(cutterPlugin); } } @@ -91,18 +91,22 @@ void PluginManager::loadPythonPlugins(const QDir &directory) { Python()->addPythonPath(directory.absolutePath().toLocal8Bit().data()); - for (const QString &fileName : directory.entryList(QDir::Files)) { - CutterPlugin *cutterPlugin = nullptr; + for (const QString &fileName : directory.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) { + if (fileName == "__pycache__") { + continue; + } QString moduleName; if (fileName.endsWith(".py")) { QStringList l = fileName.split(".py"); moduleName = l[0]; + } else { + moduleName = fileName; } - cutterPlugin = loadPythonPlugin(moduleName.toLocal8Bit().constData()); + CutterPlugin *cutterPlugin = loadPythonPlugin(moduleName.toLocal8Bit().constData()); if (!cutterPlugin) { continue; } - cutterPlugin->setupPlugin(Core()); + cutterPlugin->setupPlugin(); plugins.append(cutterPlugin); } @@ -112,20 +116,35 @@ void PluginManager::loadPythonPlugins(const QDir &directory) CutterPlugin *PluginManager::loadPythonPlugin(const char *moduleName) { PythonManager::ThreadHolder threadHolder; - PyObject *cutterPluginModule = Python()->getCutterPluginModule(); - CutterPythonPlugin *plugin = nullptr; - if (!cutterPluginModule) { - return static_cast(plugin); - } PyObject *pluginModule = PyImport_ImportModule(moduleName); if (!pluginModule) { qWarning() << "Couldn't load module for plugin:" << QString(moduleName); - PyErr_PrintEx(10); - } else { - plugin = new CutterPythonPlugin(pluginModule); + PyErr_Print(); + return nullptr; } + PyObject *createPluginFunc = PyObject_GetAttrString(pluginModule, "create_cutter_plugin"); + if (!createPluginFunc || !PyCallable_Check(createPluginFunc)) { + qWarning() << "Plugin module does not contain create_plugin() function:" << QString(moduleName); + if (createPluginFunc) { + Py_DECREF(createPluginFunc); + } + Py_DECREF(pluginModule); + return nullptr; + } + + PyObject *pluginObject = PyObject_CallFunction(createPluginFunc, nullptr); + Py_DECREF(createPluginFunc); + Py_DECREF(pluginModule); + + PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppPointerConvertible(reinterpret_cast(SbkCutterBindingsTypes[SBK_CUTTERPLUGIN_IDX]), pluginObject); + if (!pythonToCpp) { + qWarning() << "Plugin's create_plugin() function did not return an instance of CutterPlugin:" << QString(moduleName); + return nullptr; + } + CutterPlugin *plugin; + pythonToCpp(pluginObject, &plugin); return plugin; } #endif \ No newline at end of file diff --git a/src/plugins/sample-cpp/CutterSamplePlugin.cpp b/src/plugins/sample-cpp/CutterSamplePlugin.cpp index 0b45c0f3..497777ae 100644 --- a/src/plugins/sample-cpp/CutterSamplePlugin.cpp +++ b/src/plugins/sample-cpp/CutterSamplePlugin.cpp @@ -8,13 +8,8 @@ #include "common/Configuration.h" #include "MainWindow.h" -void CutterSamplePlugin::setupPlugin(CutterCore *core) +void CutterSamplePlugin::setupPlugin() { - this->core = core; - this->name = "SamplePlugin"; - this->description = "Just a sample plugin."; - this->version = "1.0"; - this->author = "xarkes"; } void CutterSamplePlugin::setupInterface(MainWindow *main) diff --git a/src/plugins/sample-cpp/CutterSamplePlugin.h b/src/plugins/sample-cpp/CutterSamplePlugin.h index 8b45be7c..32074b90 100644 --- a/src/plugins/sample-cpp/CutterSamplePlugin.h +++ b/src/plugins/sample-cpp/CutterSamplePlugin.h @@ -12,8 +12,13 @@ class CutterSamplePlugin : public QObject, CutterPlugin Q_INTERFACES(CutterPlugin) public: - void setupPlugin(CutterCore *core) override; + void setupPlugin() override; void setupInterface(MainWindow *main) override; + + const QString &getName() const { return "SamplePlugin"; } + const QString &getAuthor() const { return "xarkes"; } + const QString &getDescription() const { return "Just a sample plugin."; } + const QString &getVersion() const { return "1.0"; } }; class CutterSamplePluginWidget : public CutterDockWidget diff --git a/src/plugins/sample-python/sample_python.py b/src/plugins/sample-python/sample_python.py index 81792070..8f5412d8 100644 --- a/src/plugins/sample-python/sample_python.py +++ b/src/plugins/sample-python/sample_python.py @@ -1,6 +1,5 @@ import cutter -from cutter_plugin import CutterPlugin import CutterBindings from PySide2.QtCore import QObject, SIGNAL, Qt @@ -32,31 +31,36 @@ class FortuneWidget(CutterBindings.CutterDockWidget): layout.addWidget(button) layout.setAlignment(button, Qt.AlignHCenter) - QObject.connect(CutterBindings.CutterCore.getInstance(), SIGNAL("seekChanged(RVA)"), self.generate_fortune) + QObject.connect(cutter.core(), SIGNAL("seekChanged(RVA)"), self.generate_fortune) QObject.connect(button, SIGNAL("clicked()"), self.generate_fortune) self.show() def generate_fortune(self): fortune = cutter.cmd("fo").replace("\n", "") - res = CutterBindings.CutterCore.getInstance().cmdRaw(f"?E {fortune}") + res = cutter.core().cmdRaw(f"?E {fortune}") self.text.setText(res) -class CutterSamplePlugin(CutterPlugin): +class CutterSamplePlugin(CutterBindings.CutterPlugin): name = "SamplePlugin" description = "A sample plugin written in python." version = "1.0" author = "xarkes and thestr4ng3r :-P" - def setupInterface(self): - super().setupInterface() + def __init__(self): + super(CutterSamplePlugin, self).__init__() - self.action = QAction("Sample Python Plugin", self.main) + def setupPlugin(self): + pass + + def setupInterface(self, main): + self.action = QAction("Sample Python Plugin", main) self.action.setCheckable(True) - self.widget = FortuneWidget(self.main, self.action) # we MUST keep a reference to this! - self.main.addPluginDockWidget(self.widget, self.action) + self.widget = FortuneWidget(main, self.action) # we MUST keep a reference to this! + main.addPluginDockWidget(self.widget, self.action) -# Instantiate our plugin -plugin = CutterSamplePlugin() +# This function will be called by Cutter and should return an instance of the plugin. +def create_cutter_plugin(): + return CutterSamplePlugin() diff --git a/src/python/cutter.py b/src/python/cutter.py index 7884d705..0dd46fe6 100644 --- a/src/python/cutter.py +++ b/src/python/cutter.py @@ -1,6 +1,12 @@ import json from _cutter import * +from CutterBindings import * + def cmdj(command): - '''Execute a JSON command and return the result as a dictionnary''' + """Execute a JSON command and return the result as a dictionary""" return json.loads(cmd(command)) + + +def core(): + return CutterCore.getInstance() diff --git a/src/python/cutter_plugin.py b/src/python/cutter_plugin.py deleted file mode 100644 index 8291d647..00000000 --- a/src/python/cutter_plugin.py +++ /dev/null @@ -1,18 +0,0 @@ -from PySide2 import QtCore, QtWidgets -import shiboken2 - - -class CutterPlugin(object): - name = '' - description = '' - version = '' - author = '' - - def setupPlugin(self): - self.app = QtCore.QCoreApplication.instance() - - def setupInterface(self): - for widget in QtWidgets.QApplication.topLevelWidgets(): - if widget.objectName() == "MainWindow": - self.main = widget - break diff --git a/src/resources.qrc b/src/resources.qrc index 60e65405..4d44e914 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -86,6 +86,5 @@ img/icons/fork.svg python/cutter.py python/reg_qtres_importer.py - python/cutter_plugin.py