forked from OSchip/llvm-project
[lldb/Plugins] Introduce Scripted Interface Factory
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>
This commit is contained in:
parent
b9e57e0305
commit
3925204c1f
|
@ -288,7 +288,6 @@ LLDBSwigPythonCreateScriptedProcess
|
|||
if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
|
||||
PyErr_Cleaner py_err_cleaner(true);
|
||||
|
||||
auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
//===-- ScriptedInterface.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_INTERPRETER_SCRIPTEDINTERFACE_H
|
||||
#define LLDB_INTERPRETER_SCRIPTEDINTERFACE_H
|
||||
|
||||
#include "lldb/Core/StructuredDataImpl.h"
|
||||
#include "lldb/Target/ExecutionContext.h"
|
||||
#include "lldb/lldb-private.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace lldb_private {
|
||||
class ScriptedInterface {
|
||||
public:
|
||||
ScriptedInterface() = default;
|
||||
virtual ~ScriptedInterface() = default;
|
||||
|
||||
virtual StructuredData::GenericSP
|
||||
CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
|
||||
StructuredData::DictionarySP args_sp) = 0;
|
||||
|
||||
protected:
|
||||
StructuredData::GenericSP m_object_instance_sp;
|
||||
};
|
||||
} // namespace lldb_private
|
||||
#endif // LLDB_INTERPRETER_SCRIPTEDINTERFACE_H
|
|
@ -11,20 +11,18 @@
|
|||
|
||||
#include "lldb/Core/StructuredDataImpl.h"
|
||||
#include "lldb/Interpreter/ScriptInterpreter.h"
|
||||
#include "lldb/Interpreter/ScriptedInterface.h"
|
||||
|
||||
#include "lldb/lldb-private.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace lldb_private {
|
||||
class ScriptedProcessInterface {
|
||||
class ScriptedProcessInterface : virtual public ScriptedInterface {
|
||||
public:
|
||||
ScriptedProcessInterface() : m_object_instance_sp(nullptr) {}
|
||||
|
||||
virtual ~ScriptedProcessInterface() = default;
|
||||
|
||||
virtual StructuredData::GenericSP
|
||||
CreatePluginObject(const llvm::StringRef class_name, lldb::TargetSP target_sp,
|
||||
StructuredData::DictionarySP args_sp) {
|
||||
StructuredData::GenericSP
|
||||
CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
|
||||
StructuredData::DictionarySP args_sp) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -59,9 +57,6 @@ public:
|
|||
virtual lldb::pid_t GetProcessID() { return LLDB_INVALID_PROCESS_ID; }
|
||||
|
||||
virtual bool IsAlive() { return true; }
|
||||
|
||||
private:
|
||||
StructuredData::ObjectSP m_object_instance_sp;
|
||||
};
|
||||
} // namespace lldb_private
|
||||
|
||||
|
|
|
@ -109,8 +109,10 @@ ScriptedProcess::ScriptedProcess(
|
|||
return;
|
||||
}
|
||||
|
||||
StructuredData::ObjectSP object_sp = GetInterface().CreatePluginObject(
|
||||
m_scripted_process_info.GetClassName().c_str(), target_sp,
|
||||
ExecutionContext exe_ctx(target_sp, /*get_process=*/false);
|
||||
|
||||
StructuredData::GenericSP object_sp = GetInterface().CreatePluginObject(
|
||||
m_scripted_process_info.GetClassName().c_str(), exe_ctx,
|
||||
m_scripted_process_info.GetDictionarySP());
|
||||
|
||||
if (!object_sp || !object_sp->IsValid()) {
|
||||
|
|
|
@ -11,6 +11,7 @@ add_lldb_library(lldbPluginScriptInterpreterPython PLUGIN
|
|||
PythonDataObjects.cpp
|
||||
PythonReadline.cpp
|
||||
ScriptInterpreterPython.cpp
|
||||
ScriptedPythonInterface.cpp
|
||||
ScriptedProcessPythonInterface.cpp
|
||||
SWIGPythonBridge.cpp
|
||||
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
|
||||
#if LLDB_ENABLE_PYTHON
|
||||
|
||||
#include "ScriptedProcessPythonInterface.h"
|
||||
|
||||
#include "lldb/Breakpoint/BreakpointOptions.h"
|
||||
#include "lldb/Core/IOHandler.h"
|
||||
#include "lldb/Core/StructuredDataImpl.h"
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lldb/Host/Config.h"
|
||||
#include "lldb/Utility/Log.h"
|
||||
#include "lldb/Utility/Logging.h"
|
||||
#include "lldb/lldb-enumerations.h"
|
||||
|
||||
#if LLDB_ENABLE_PYTHON
|
||||
|
@ -23,30 +25,33 @@ using namespace lldb_private;
|
|||
using namespace lldb_private::python;
|
||||
using Locker = ScriptInterpreterPythonImpl::Locker;
|
||||
|
||||
ScriptedProcessPythonInterface::ScriptedProcessPythonInterface(
|
||||
ScriptInterpreterPythonImpl &interpreter)
|
||||
: ScriptedProcessInterface(), ScriptedPythonInterface(interpreter) {}
|
||||
|
||||
StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject(
|
||||
const llvm::StringRef class_name, lldb::TargetSP target_sp,
|
||||
llvm::StringRef class_name, ExecutionContext &exe_ctx,
|
||||
StructuredData::DictionarySP args_sp) {
|
||||
if (class_name.empty())
|
||||
return {};
|
||||
|
||||
std::string error_string;
|
||||
TargetSP target_sp = exe_ctx.GetTargetSP();
|
||||
StructuredDataImpl *args_impl = nullptr;
|
||||
if (args_sp) {
|
||||
args_impl = new StructuredDataImpl();
|
||||
args_impl->SetObjectSP(args_sp);
|
||||
}
|
||||
std::string error_string;
|
||||
|
||||
void *ret_val;
|
||||
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
|
||||
Locker::FreeLock);
|
||||
|
||||
{
|
||||
void *ret_val = LLDBSwigPythonCreateScriptedProcess(
|
||||
class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp,
|
||||
args_impl, error_string);
|
||||
|
||||
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
|
||||
Locker::FreeLock);
|
||||
|
||||
ret_val = LLDBSwigPythonCreateScriptedProcess(
|
||||
class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp,
|
||||
args_impl, error_string);
|
||||
}
|
||||
if (!ret_val)
|
||||
return {};
|
||||
|
||||
m_object_instance_sp =
|
||||
StructuredData::GenericSP(new StructuredPythonObject(ret_val));
|
||||
|
@ -63,244 +68,117 @@ Status ScriptedProcessPythonInterface::Resume() {
|
|||
}
|
||||
|
||||
bool ScriptedProcessPythonInterface::ShouldStop() {
|
||||
llvm::Optional<unsigned long long> should_stop =
|
||||
GetGenericInteger("should_stop");
|
||||
Status error;
|
||||
StructuredData::ObjectSP obj = Dispatch("is_alive", error);
|
||||
|
||||
if (!should_stop)
|
||||
auto error_with_message = [](llvm::StringRef message) {
|
||||
LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS),
|
||||
"ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data());
|
||||
return false;
|
||||
};
|
||||
|
||||
return static_cast<bool>(*should_stop);
|
||||
if (!obj || !obj->IsValid() || error.Fail()) {
|
||||
return error_with_message(llvm::Twine("Null or invalid object (" +
|
||||
llvm::Twine(error.AsCString()) +
|
||||
llvm::Twine(")."))
|
||||
.str());
|
||||
}
|
||||
|
||||
return obj->GetBooleanValue();
|
||||
}
|
||||
|
||||
Status ScriptedProcessPythonInterface::Stop() {
|
||||
return GetStatusFromMethod("stop");
|
||||
}
|
||||
|
||||
Status ScriptedProcessPythonInterface::GetStatusFromMethod(
|
||||
llvm::StringRef method_name) {
|
||||
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
|
||||
Locker::FreeLock);
|
||||
|
||||
if (!m_object_instance_sp)
|
||||
return Status("Python object ill-formed.");
|
||||
|
||||
if (!m_object_instance_sp)
|
||||
return Status("Cannot convert Python object to StructuredData::Generic.");
|
||||
PythonObject implementor(PyRefType::Borrowed,
|
||||
(PyObject *)m_object_instance_sp->GetValue());
|
||||
|
||||
if (!implementor.IsAllocated())
|
||||
return Status("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 Status("Python method not allocated.");
|
||||
|
||||
if (PyCallable_Check(pmeth.get()) == 0) {
|
||||
if (PyErr_Occurred())
|
||||
PyErr_Clear();
|
||||
return Status("Python method not callable.");
|
||||
}
|
||||
|
||||
if (PyErr_Occurred())
|
||||
PyErr_Clear();
|
||||
|
||||
PythonObject py_return(PyRefType::Owned,
|
||||
PyObject_CallMethod(implementor.get(),
|
||||
method_name.str().c_str(),
|
||||
nullptr));
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
return Status("Python method could not be called.");
|
||||
}
|
||||
|
||||
if (PyObject *py_ret_ptr = py_return.get()) {
|
||||
lldb::SBError *sb_error =
|
||||
(lldb::SBError *)LLDBSWIGPython_CastPyObjectToSBError(py_ret_ptr);
|
||||
|
||||
if (!sb_error)
|
||||
return Status("Couldn't cast lldb::SBError to lldb::Status.");
|
||||
|
||||
Status status = m_interpreter.GetStatusFromSBError(*sb_error);
|
||||
|
||||
if (status.Fail())
|
||||
return Status("error: %s", status.AsCString());
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
return Status("Returned object is null.");
|
||||
}
|
||||
|
||||
llvm::Optional<unsigned long long>
|
||||
ScriptedProcessPythonInterface::GetGenericInteger(llvm::StringRef method_name) {
|
||||
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
|
||||
Locker::FreeLock);
|
||||
|
||||
if (!m_object_instance_sp)
|
||||
return llvm::None;
|
||||
|
||||
if (!m_object_instance_sp)
|
||||
return llvm::None;
|
||||
PythonObject implementor(PyRefType::Borrowed,
|
||||
(PyObject *)m_object_instance_sp->GetValue());
|
||||
|
||||
if (!implementor.IsAllocated())
|
||||
return llvm::None;
|
||||
|
||||
PythonObject pmeth(
|
||||
PyRefType::Owned,
|
||||
PyObject_GetAttrString(implementor.get(), method_name.str().c_str()));
|
||||
|
||||
if (PyErr_Occurred())
|
||||
PyErr_Clear();
|
||||
|
||||
if (!pmeth.IsAllocated())
|
||||
return llvm::None;
|
||||
|
||||
if (PyCallable_Check(pmeth.get()) == 0) {
|
||||
if (PyErr_Occurred())
|
||||
PyErr_Clear();
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
if (PyErr_Occurred())
|
||||
PyErr_Clear();
|
||||
|
||||
PythonObject py_return(PyRefType::Owned,
|
||||
PyObject_CallMethod(implementor.get(),
|
||||
method_name.str().c_str(),
|
||||
nullptr));
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
if (!py_return.get())
|
||||
return llvm::None;
|
||||
|
||||
llvm::Expected<unsigned long long> size = py_return.AsUnsignedLongLong();
|
||||
// FIXME: Handle error.
|
||||
if (!size)
|
||||
return llvm::None;
|
||||
|
||||
return *size;
|
||||
}
|
||||
|
||||
lldb::MemoryRegionInfoSP
|
||||
ScriptedProcessPythonInterface::GetMemoryRegionContainingAddress(
|
||||
lldb::addr_t address) {
|
||||
// TODO: Implement
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
StructuredData::DictionarySP
|
||||
ScriptedProcessPythonInterface::GetThreadWithID(lldb::tid_t tid) {
|
||||
// TODO: Implement
|
||||
return nullptr;
|
||||
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
|
||||
Locker::FreeLock);
|
||||
|
||||
auto error_with_message = [](llvm::StringRef message) {
|
||||
LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS),
|
||||
"ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data());
|
||||
return StructuredData::DictionarySP();
|
||||
};
|
||||
|
||||
Status error;
|
||||
StructuredData::ObjectSP obj = Dispatch("get_thread_with_id", error, tid);
|
||||
|
||||
if (!obj || !obj->IsValid() || error.Fail()) {
|
||||
return error_with_message(llvm::Twine("Null or invalid object (" +
|
||||
llvm::Twine(error.AsCString()) +
|
||||
llvm::Twine(")."))
|
||||
.str());
|
||||
}
|
||||
|
||||
StructuredData::DictionarySP dict{obj->GetAsDictionary()};
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
StructuredData::DictionarySP
|
||||
ScriptedProcessPythonInterface::GetRegistersForThread(lldb::tid_t tid) {
|
||||
// TODO: Implement
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
lldb::DataExtractorSP ScriptedProcessPythonInterface::ReadMemoryAtAddress(
|
||||
lldb::addr_t address, size_t size, Status &error) {
|
||||
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
|
||||
Locker::FreeLock);
|
||||
|
||||
auto error_with_message = [&error](llvm::StringRef message) {
|
||||
error.SetErrorString(message);
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
static char callee_name[] = "read_memory_at_address";
|
||||
std::string param_format = GetPythonValueFormatString(address);
|
||||
param_format += GetPythonValueFormatString(size);
|
||||
|
||||
if (!m_object_instance_sp)
|
||||
return error_with_message("Python object ill-formed.");
|
||||
|
||||
if (!m_object_instance_sp)
|
||||
return error_with_message("Python method not callable.");
|
||||
|
||||
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(), callee_name));
|
||||
|
||||
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();
|
||||
|
||||
PythonObject py_return(PyRefType::Owned,
|
||||
PyObject_CallMethod(implementor.get(), callee_name,
|
||||
param_format.c_str(), address,
|
||||
size));
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
return error_with_message("Python method could not be called.");
|
||||
}
|
||||
|
||||
if (PyObject *py_ret_ptr = py_return.get()) {
|
||||
lldb::SBData *sb_data =
|
||||
(lldb::SBData *)LLDBSWIGPython_CastPyObjectToSBData(py_ret_ptr);
|
||||
|
||||
if (!sb_data)
|
||||
return error_with_message(
|
||||
"Couldn't cast lldb::SBData to lldb::DataExtractor.");
|
||||
|
||||
return m_interpreter.GetDataExtractorFromSBData(*sb_data);
|
||||
}
|
||||
|
||||
return error_with_message("Returned object is null.");
|
||||
return Dispatch<lldb::DataExtractorSP>("read_memory_at_address", error,
|
||||
address, size);
|
||||
}
|
||||
|
||||
StructuredData::DictionarySP ScriptedProcessPythonInterface::GetLoadedImages() {
|
||||
// TODO: Implement
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
lldb::pid_t ScriptedProcessPythonInterface::GetProcessID() {
|
||||
llvm::Optional<unsigned long long> pid = GetGenericInteger("get_process_id");
|
||||
return (!pid) ? LLDB_INVALID_PROCESS_ID : *pid;
|
||||
Status error;
|
||||
StructuredData::ObjectSP obj = Dispatch("get_process_id", error);
|
||||
|
||||
auto error_with_message = [](llvm::StringRef message) {
|
||||
LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS),
|
||||
"ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data());
|
||||
return LLDB_INVALID_PROCESS_ID;
|
||||
};
|
||||
|
||||
if (!obj || !obj->IsValid() || error.Fail()) {
|
||||
return error_with_message(llvm::Twine("Null or invalid object (" +
|
||||
llvm::Twine(error.AsCString()) +
|
||||
llvm::Twine(")."))
|
||||
.str());
|
||||
}
|
||||
|
||||
return obj->GetIntegerValue(LLDB_INVALID_PROCESS_ID);
|
||||
}
|
||||
|
||||
bool ScriptedProcessPythonInterface::IsAlive() {
|
||||
llvm::Optional<unsigned long long> is_alive = GetGenericInteger("is_alive");
|
||||
Status error;
|
||||
StructuredData::ObjectSP obj = Dispatch("is_alive", error);
|
||||
|
||||
if (!is_alive)
|
||||
auto error_with_message = [](llvm::StringRef message) {
|
||||
LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS),
|
||||
"ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data());
|
||||
return false;
|
||||
};
|
||||
|
||||
return static_cast<bool>(*is_alive);
|
||||
if (!obj || !obj->IsValid() || error.Fail()) {
|
||||
return error_with_message(llvm::Twine("Null or invalid object (" +
|
||||
llvm::Twine(error.AsCString()) +
|
||||
llvm::Twine(")."))
|
||||
.str());
|
||||
}
|
||||
|
||||
return obj->GetBooleanValue();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,17 +13,18 @@
|
|||
|
||||
#if LLDB_ENABLE_PYTHON
|
||||
|
||||
#include "ScriptedPythonInterface.h"
|
||||
#include "lldb/Interpreter/ScriptedProcessInterface.h"
|
||||
|
||||
namespace lldb_private {
|
||||
class ScriptInterpreterPythonImpl;
|
||||
class ScriptedProcessPythonInterface : public ScriptedProcessInterface {
|
||||
class ScriptedProcessPythonInterface : public ScriptedProcessInterface,
|
||||
public ScriptedPythonInterface {
|
||||
public:
|
||||
ScriptedProcessPythonInterface(ScriptInterpreterPythonImpl &interpreter)
|
||||
: ScriptedProcessInterface(), m_interpreter(interpreter) {}
|
||||
ScriptedProcessPythonInterface(ScriptInterpreterPythonImpl &interpreter);
|
||||
|
||||
StructuredData::GenericSP
|
||||
CreatePluginObject(const llvm::StringRef class_name, lldb::TargetSP target_sp,
|
||||
CreatePluginObject(const llvm::StringRef class_name,
|
||||
ExecutionContext &exe_ctx,
|
||||
StructuredData::DictionarySP args_sp) override;
|
||||
|
||||
Status Launch() override;
|
||||
|
@ -49,16 +50,6 @@ public:
|
|||
lldb::pid_t GetProcessID() override;
|
||||
|
||||
bool IsAlive() override;
|
||||
|
||||
protected:
|
||||
llvm::Optional<unsigned long long>
|
||||
GetGenericInteger(llvm::StringRef method_name);
|
||||
Status GetStatusFromMethod(llvm::StringRef method_name);
|
||||
|
||||
private:
|
||||
// The lifetime is managed by the ScriptInterpreter
|
||||
ScriptInterpreterPythonImpl &m_interpreter;
|
||||
StructuredData::GenericSP m_object_instance_sp;
|
||||
};
|
||||
} // namespace lldb_private
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
//===-- ScriptedPythonInterface.cpp ---------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lldb/Host/Config.h"
|
||||
#include "lldb/Utility/Log.h"
|
||||
#include "lldb/Utility/Logging.h"
|
||||
#include "lldb/lldb-enumerations.h"
|
||||
|
||||
#if LLDB_ENABLE_PYTHON
|
||||
|
||||
// LLDB Python header must be included first
|
||||
#include "lldb-python.h"
|
||||
|
||||
#include "SWIGPythonBridge.h"
|
||||
#include "ScriptInterpreterPythonImpl.h"
|
||||
#include "ScriptedPythonInterface.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
ScriptedPythonInterface::ScriptedPythonInterface(
|
||||
ScriptInterpreterPythonImpl &interpreter)
|
||||
: ScriptedInterface(), m_interpreter(interpreter) {}
|
||||
|
||||
Status
|
||||
ScriptedPythonInterface::GetStatusFromMethod(llvm::StringRef method_name) {
|
||||
Status error;
|
||||
Dispatch<Status>(method_name, error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,155 @@
|
|||
//===-- 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
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
|
||||
#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h"
|
||||
#include "lldb/API/SBError.h"
|
||||
|
||||
#include "lldb/Host/FileSystem.h"
|
||||
#include "lldb/Host/HostInfo.h"
|
||||
|
||||
|
|
Loading…
Reference in New Issue