Path: blob/main/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
39688 views
//===-- ScriptedPythonInterface.h -------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H9#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H1011#if LLDB_ENABLE_PYTHON1213#include <optional>14#include <sstream>15#include <tuple>16#include <type_traits>17#include <utility>1819#include "lldb/Host/Config.h"20#include "lldb/Interpreter/Interfaces/ScriptedInterface.h"21#include "lldb/Utility/DataBufferHeap.h"2223#include "../PythonDataObjects.h"24#include "../SWIGPythonBridge.h"25#include "../ScriptInterpreterPythonImpl.h"2627namespace lldb_private {28class ScriptInterpreterPythonImpl;29class ScriptedPythonInterface : virtual public ScriptedInterface {30public:31ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter);32~ScriptedPythonInterface() override = default;3334enum class AbstractMethodCheckerCases {35eNotImplemented,36eNotAllocated,37eNotCallable,38eValid39};4041llvm::Expected<std::map<llvm::StringLiteral, AbstractMethodCheckerCases>>42CheckAbstractMethodImplementation(43const python::PythonDictionary &class_dict) const {4445using namespace python;4647std::map<llvm::StringLiteral, AbstractMethodCheckerCases> checker;48#define SET_ERROR_AND_CONTINUE(method_name, error) \49{ \50checker[method_name] = error; \51continue; \52}5354for (const llvm::StringLiteral &method_name : GetAbstractMethods()) {55if (!class_dict.HasKey(method_name))56SET_ERROR_AND_CONTINUE(method_name,57AbstractMethodCheckerCases::eNotImplemented)58auto callable_or_err = class_dict.GetItem(method_name);59if (!callable_or_err)60SET_ERROR_AND_CONTINUE(method_name,61AbstractMethodCheckerCases::eNotAllocated)62if (!PythonCallable::Check(callable_or_err.get().get()))63SET_ERROR_AND_CONTINUE(method_name,64AbstractMethodCheckerCases::eNotCallable)65checker[method_name] = AbstractMethodCheckerCases::eValid;66}6768#undef HANDLE_ERROR6970return checker;71}7273template <typename... Args>74llvm::Expected<StructuredData::GenericSP>75CreatePluginObject(llvm::StringRef class_name,76StructuredData::Generic *script_obj, Args... args) {77using namespace python;78using Locker = ScriptInterpreterPythonImpl::Locker;7980auto create_error = [](std::string message) {81return llvm::createStringError(llvm::inconvertibleErrorCode(), message);82};8384bool has_class_name = !class_name.empty();85bool has_interpreter_dict =86!(llvm::StringRef(m_interpreter.GetDictionaryName()).empty());87if (!has_class_name && !has_interpreter_dict && !script_obj) {88if (!has_class_name)89return create_error("Missing script class name.");90else if (!has_interpreter_dict)91return create_error("Invalid script interpreter dictionary.");92else93return create_error("Missing scripting object.");94}9596Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,97Locker::FreeLock);9899PythonObject result = {};100101if (script_obj) {102result = PythonObject(PyRefType::Borrowed,103static_cast<PyObject *>(script_obj->GetValue()));104} else {105auto dict =106PythonModule::MainModule().ResolveName<python::PythonDictionary>(107m_interpreter.GetDictionaryName());108if (!dict.IsAllocated())109return create_error(110llvm::formatv("Could not find interpreter dictionary: %s",111m_interpreter.GetDictionaryName()));112113auto init =114PythonObject::ResolveNameWithDictionary<python::PythonCallable>(115class_name, dict);116if (!init.IsAllocated())117return create_error(llvm::formatv("Could not find script class: {0}",118class_name.data()));119120std::tuple<Args...> original_args = std::forward_as_tuple(args...);121auto transformed_args = TransformArgs(original_args);122123std::string error_string;124llvm::Expected<PythonCallable::ArgInfo> arg_info = init.GetArgInfo();125if (!arg_info) {126llvm::handleAllErrors(127arg_info.takeError(),128[&](PythonException &E) { error_string.append(E.ReadBacktrace()); },129[&](const llvm::ErrorInfoBase &E) {130error_string.append(E.message());131});132return llvm::createStringError(llvm::inconvertibleErrorCode(),133error_string);134}135136llvm::Expected<PythonObject> expected_return_object =137create_error("Resulting object is not initialized.");138139std::apply(140[&init, &expected_return_object](auto &&...args) {141llvm::consumeError(expected_return_object.takeError());142expected_return_object = init(args...);143},144transformed_args);145146if (!expected_return_object)147return expected_return_object.takeError();148result = expected_return_object.get();149}150151if (!result.IsValid())152return create_error("Resulting object is not a valid Python Object.");153if (!result.HasAttribute("__class__"))154return create_error("Resulting object doesn't have '__class__' member.");155156PythonObject obj_class = result.GetAttributeValue("__class__");157if (!obj_class.IsValid())158return create_error("Resulting class object is not a valid.");159if (!obj_class.HasAttribute("__name__"))160return create_error(161"Resulting object class doesn't have '__name__' member.");162PythonString obj_class_name =163obj_class.GetAttributeValue("__name__").AsType<PythonString>();164165PythonObject object_class_mapping_proxy =166obj_class.GetAttributeValue("__dict__");167if (!obj_class.HasAttribute("__dict__"))168return create_error(169"Resulting object class doesn't have '__dict__' member.");170171PythonCallable dict_converter = PythonModule::BuiltinsModule()172.ResolveName("dict")173.AsType<PythonCallable>();174if (!dict_converter.IsAllocated())175return create_error(176"Python 'builtins' module doesn't have 'dict' class.");177178PythonDictionary object_class_dict =179dict_converter(object_class_mapping_proxy).AsType<PythonDictionary>();180if (!object_class_dict.IsAllocated())181return create_error("Coudn't create dictionary from resulting object "182"class mapping proxy object.");183184auto checker_or_err = CheckAbstractMethodImplementation(object_class_dict);185if (!checker_or_err)186return checker_or_err.takeError();187188for (const auto &method_checker : *checker_or_err)189switch (method_checker.second) {190case AbstractMethodCheckerCases::eNotImplemented:191LLDB_LOG(GetLog(LLDBLog::Script),192"Abstract method {0}.{1} not implemented.",193obj_class_name.GetString(), method_checker.first);194break;195case AbstractMethodCheckerCases::eNotAllocated:196LLDB_LOG(GetLog(LLDBLog::Script),197"Abstract method {0}.{1} not allocated.",198obj_class_name.GetString(), method_checker.first);199break;200case AbstractMethodCheckerCases::eNotCallable:201LLDB_LOG(GetLog(LLDBLog::Script),202"Abstract method {0}.{1} not callable.",203obj_class_name.GetString(), method_checker.first);204break;205case AbstractMethodCheckerCases::eValid:206LLDB_LOG(GetLog(LLDBLog::Script),207"Abstract method {0}.{1} implemented & valid.",208obj_class_name.GetString(), method_checker.first);209break;210}211212for (const auto &method_checker : *checker_or_err)213if (method_checker.second != AbstractMethodCheckerCases::eValid)214return create_error(215llvm::formatv("Abstract method {0}.{1} missing. Enable lldb "216"script log for more details.",217obj_class_name.GetString(), method_checker.first));218219m_object_instance_sp = StructuredData::GenericSP(220new StructuredPythonObject(std::move(result)));221return m_object_instance_sp;222}223224protected:225template <typename T = StructuredData::ObjectSP>226T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {227return p.CreateStructuredObject();228}229230template <typename T = StructuredData::ObjectSP, typename... Args>231T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) {232using namespace python;233using Locker = ScriptInterpreterPythonImpl::Locker;234235std::string caller_signature =236llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +237llvm::Twine(method_name) + llvm::Twine(")"))238.str();239if (!m_object_instance_sp)240return ErrorWithMessage<T>(caller_signature, "Python object ill-formed",241error);242243Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,244Locker::FreeLock);245246PythonObject implementor(PyRefType::Borrowed,247(PyObject *)m_object_instance_sp->GetValue());248249if (!implementor.IsAllocated())250return llvm::is_contained(GetAbstractMethods(), method_name)251? ErrorWithMessage<T>(caller_signature,252"Python implementor not allocated.",253error)254: T{};255256std::tuple<Args...> original_args = std::forward_as_tuple(args...);257auto transformed_args = TransformArgs(original_args);258259llvm::Expected<PythonObject> expected_return_object =260llvm::make_error<llvm::StringError>("Not initialized.",261llvm::inconvertibleErrorCode());262std::apply(263[&implementor, &method_name, &expected_return_object](auto &&...args) {264llvm::consumeError(expected_return_object.takeError());265expected_return_object =266implementor.CallMethod(method_name.data(), args...);267},268transformed_args);269270if (llvm::Error e = expected_return_object.takeError()) {271error.SetErrorString(llvm::toString(std::move(e)).c_str());272return ErrorWithMessage<T>(caller_signature,273"Python method could not be called.", error);274}275276PythonObject py_return = std::move(expected_return_object.get());277278// Now that we called the python method with the transformed arguments,279// we need to interate again over both the original and transformed280// parameter pack, and transform back the parameter that were passed in281// the original parameter pack as references or pointers.282if (sizeof...(Args) > 0)283if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))284return ErrorWithMessage<T>(285caller_signature,286"Couldn't re-assign reference and pointer arguments.", error);287288if (!py_return.IsAllocated())289return {};290return ExtractValueFromPythonObject<T>(py_return, error);291}292293template <typename... Args>294Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {295Status error;296Dispatch<Status>(method_name, error, std::forward<Args>(args)...);297298return error;299}300301template <typename T> T Transform(T object) {302// No Transformation for generic usage303return {object};304}305306python::PythonObject Transform(bool arg) {307// Boolean arguments need to be turned into python objects.308return python::PythonBoolean(arg);309}310311python::PythonObject Transform(Status arg) {312return python::SWIGBridge::ToSWIGWrapper(arg);313}314315python::PythonObject Transform(const StructuredDataImpl &arg) {316return python::SWIGBridge::ToSWIGWrapper(arg);317}318319python::PythonObject Transform(lldb::ExecutionContextRefSP arg) {320return python::SWIGBridge::ToSWIGWrapper(arg);321}322323python::PythonObject Transform(lldb::ProcessSP arg) {324return python::SWIGBridge::ToSWIGWrapper(arg);325}326327python::PythonObject Transform(lldb::ThreadPlanSP arg) {328return python::SWIGBridge::ToSWIGWrapper(arg);329}330331python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {332return python::SWIGBridge::ToSWIGWrapper(arg);333}334335python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {336return python::SWIGBridge::ToSWIGWrapper(arg);337}338339python::PythonObject Transform(Event *arg) {340return python::SWIGBridge::ToSWIGWrapper(arg);341}342343python::PythonObject Transform(lldb::StreamSP arg) {344return python::SWIGBridge::ToSWIGWrapper(arg.get());345}346347python::PythonObject Transform(lldb::DataExtractorSP arg) {348return python::SWIGBridge::ToSWIGWrapper(arg);349}350351template <typename T, typename U>352void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {353// If U is not a PythonObject, don't touch it!354return;355}356357template <typename T>358void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,359Status &error) {360original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);361}362363void ReverseTransform(bool &original_arg,364python::PythonObject transformed_arg, Status &error) {365python::PythonBoolean boolean_arg = python::PythonBoolean(366python::PyRefType::Borrowed, transformed_arg.get());367if (boolean_arg.IsValid())368original_arg = boolean_arg.GetValue();369else370error.SetErrorString(371llvm::formatv("{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION)372.str());373}374375template <std::size_t... I, typename... Args>376auto TransformTuple(const std::tuple<Args...> &args,377std::index_sequence<I...>) {378return std::make_tuple(Transform(std::get<I>(args))...);379}380381// This will iterate over the Dispatch parameter pack and replace in-place382// every `lldb_private` argument that has a SB counterpart.383template <typename... Args>384auto TransformArgs(const std::tuple<Args...> &args) {385return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());386}387388template <typename T, typename U>389void TransformBack(T &original_arg, U transformed_arg, Status &error) {390ReverseTransform(original_arg, transformed_arg, error);391}392393template <std::size_t... I, typename... Ts, typename... Us>394bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,395std::tuple<Us...> &transformed_args,396std::index_sequence<I...>) {397Status error;398(TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),399error),400...);401return error.Success();402}403404template <typename... Ts, typename... Us>405bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,406std::tuple<Us...> &transformed_args) {407if (sizeof...(Ts) != sizeof...(Us))408return false;409410return ReassignPtrsOrRefsArgs(original_args, transformed_args,411std::make_index_sequence<sizeof...(Ts)>());412}413414template <typename T, typename... Args>415void FormatArgs(std::string &fmt, T arg, Args... args) const {416FormatArgs(fmt, arg);417FormatArgs(fmt, args...);418}419420template <typename T> void FormatArgs(std::string &fmt, T arg) const {421fmt += python::PythonFormat<T>::format;422}423424void FormatArgs(std::string &fmt) const {}425426// The lifetime is managed by the ScriptInterpreter427ScriptInterpreterPythonImpl &m_interpreter;428};429430template <>431StructuredData::ArraySP432ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(433python::PythonObject &p, Status &error);434435template <>436StructuredData::DictionarySP437ScriptedPythonInterface::ExtractValueFromPythonObject<438StructuredData::DictionarySP>(python::PythonObject &p, Status &error);439440template <>441Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(442python::PythonObject &p, Status &error);443444template <>445Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>(446python::PythonObject &p, Status &error);447448template <>449lldb::StreamSP450ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(451python::PythonObject &p, Status &error);452453template <>454lldb::BreakpointSP455ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(456python::PythonObject &p, Status &error);457458template <>459lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<460lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);461462template <>463lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<464lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);465466template <>467lldb::DataExtractorSP468ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(469python::PythonObject &p, Status &error);470471template <>472std::optional<MemoryRegionInfo>473ScriptedPythonInterface::ExtractValueFromPythonObject<474std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);475476} // namespace lldb_private477478#endif // LLDB_ENABLE_PYTHON479#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H480481482