
This patch splits the previous `ScriptedProcessPythonInterface` into multiple specific classes: 1. The `ScriptedInterface` abstract class that carries the interface instance object and its virtual pure abstract creation method. 2. The `ScriptedPythonInterface` that holds a generic `Dispatch` method that can be used by various interfaces to call python methods and also keeps a reference to the Python Script Interpreter instance. 3. The `ScriptedProcessInterface` that describes the base Scripted Process model with all the methods used in the underlying script. All these components are used to refactor the `ScriptedProcessPythonInterface` class, making it more modular. This patch is also a requirement for the upcoming work on `ScriptedThread`. Differential Revision: https://reviews.llvm.org/D107521 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
156 lines
5.0 KiB
C++
156 lines
5.0 KiB
C++
//===-- ScriptedPythonInterface.h -------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H
|
|
#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H
|
|
|
|
#include "lldb/Host/Config.h"
|
|
|
|
#if LLDB_ENABLE_PYTHON
|
|
|
|
#include "lldb/Interpreter/ScriptedInterface.h"
|
|
#include "lldb/Utility/DataBufferHeap.h"
|
|
|
|
#include "PythonDataObjects.h"
|
|
#include "SWIGPythonBridge.h"
|
|
#include "ScriptInterpreterPythonImpl.h"
|
|
|
|
namespace lldb_private {
|
|
class ScriptInterpreterPythonImpl;
|
|
class ScriptedPythonInterface : virtual public ScriptedInterface {
|
|
public:
|
|
ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter);
|
|
virtual ~ScriptedPythonInterface() = default;
|
|
|
|
protected:
|
|
template <typename T = StructuredData::ObjectSP>
|
|
T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {
|
|
return p.CreateStructuredObject();
|
|
}
|
|
|
|
template <>
|
|
Status ExtractValueFromPythonObject<Status>(python::PythonObject &p,
|
|
Status &error) {
|
|
if (lldb::SBError *sb_error = reinterpret_cast<lldb::SBError *>(
|
|
LLDBSWIGPython_CastPyObjectToSBError(p.get())))
|
|
error = m_interpreter.GetStatusFromSBError(*sb_error);
|
|
else
|
|
error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status.");
|
|
|
|
return error;
|
|
}
|
|
|
|
template <>
|
|
lldb::DataExtractorSP
|
|
ExtractValueFromPythonObject<lldb::DataExtractorSP>(python::PythonObject &p,
|
|
Status &error) {
|
|
lldb::SBData *sb_data = reinterpret_cast<lldb::SBData *>(
|
|
LLDBSWIGPython_CastPyObjectToSBData(p.get()));
|
|
|
|
if (!sb_data) {
|
|
error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status.");
|
|
return nullptr;
|
|
}
|
|
|
|
return m_interpreter.GetDataExtractorFromSBData(*sb_data);
|
|
}
|
|
|
|
template <typename T = StructuredData::ObjectSP, typename... Args>
|
|
T Dispatch(llvm::StringRef method_name, Status &error, Args... args) {
|
|
using namespace python;
|
|
using Locker = ScriptInterpreterPythonImpl::Locker;
|
|
|
|
auto error_with_message = [&method_name, &error](llvm::StringRef message) {
|
|
error.SetErrorStringWithFormatv(
|
|
"ScriptedPythonInterface::{0} ({1}) ERROR = {2}", __FUNCTION__,
|
|
method_name, message);
|
|
return T();
|
|
};
|
|
|
|
if (!m_object_instance_sp)
|
|
return error_with_message("Python object ill-formed");
|
|
|
|
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
|
|
Locker::FreeLock);
|
|
|
|
PythonObject implementor(PyRefType::Borrowed,
|
|
(PyObject *)m_object_instance_sp->GetValue());
|
|
|
|
if (!implementor.IsAllocated())
|
|
return error_with_message("Python implementor not allocated.");
|
|
|
|
PythonObject pmeth(
|
|
PyRefType::Owned,
|
|
PyObject_GetAttrString(implementor.get(), method_name.str().c_str()));
|
|
|
|
if (PyErr_Occurred())
|
|
PyErr_Clear();
|
|
|
|
if (!pmeth.IsAllocated())
|
|
return error_with_message("Python method not allocated.");
|
|
|
|
if (PyCallable_Check(pmeth.get()) == 0) {
|
|
if (PyErr_Occurred())
|
|
PyErr_Clear();
|
|
return error_with_message("Python method not callable.");
|
|
}
|
|
|
|
if (PyErr_Occurred())
|
|
PyErr_Clear();
|
|
|
|
// TODO: make `const char *` when removing support for Python 2.
|
|
char *format = nullptr;
|
|
std::string format_buffer;
|
|
|
|
if (sizeof...(Args) > 0) {
|
|
FormatArgs(format_buffer, args...);
|
|
// TODO: make `const char *` when removing support for Python 2.
|
|
format = const_cast<char *>(format_buffer.c_str());
|
|
}
|
|
|
|
// TODO: make `const char *` when removing support for Python 2.
|
|
PythonObject py_return(
|
|
PyRefType::Owned,
|
|
PyObject_CallMethod(implementor.get(),
|
|
const_cast<char *>(method_name.data()), format,
|
|
args...));
|
|
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
PyErr_Clear();
|
|
return error_with_message("Python method could not be called.");
|
|
}
|
|
|
|
if (!py_return.IsAllocated())
|
|
return error_with_message("Returned object is null.");
|
|
|
|
return ExtractValueFromPythonObject<T>(py_return, error);
|
|
}
|
|
|
|
Status GetStatusFromMethod(llvm::StringRef method_name);
|
|
|
|
template <typename T, typename... Args>
|
|
void FormatArgs(std::string &fmt, T arg, Args... args) const {
|
|
FormatArgs(fmt, arg);
|
|
FormatArgs(fmt, args...);
|
|
}
|
|
|
|
template <typename T> void FormatArgs(std::string &fmt, T arg) const {
|
|
fmt += GetPythonValueFormatString(arg);
|
|
}
|
|
|
|
void FormatArgs(std::string &fmt) const {}
|
|
|
|
// The lifetime is managed by the ScriptInterpreter
|
|
ScriptInterpreterPythonImpl &m_interpreter;
|
|
};
|
|
} // namespace lldb_private
|
|
|
|
#endif // LLDB_ENABLE_PYTHON
|
|
#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H
|