1 //===-- ScriptedPythonInterface.h -------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H
10 #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H
11 
12 #include "lldb/Host/Config.h"
13 
14 #if LLDB_ENABLE_PYTHON
15 
16 #include "lldb/Interpreter/ScriptedInterface.h"
17 #include "lldb/Utility/DataBufferHeap.h"
18 
19 #include "PythonDataObjects.h"
20 #include "SWIGPythonBridge.h"
21 #include "ScriptInterpreterPythonImpl.h"
22 
23 namespace lldb_private {
24 class ScriptInterpreterPythonImpl;
25 class ScriptedPythonInterface : virtual public ScriptedInterface {
26 public:
27   ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter);
28   virtual ~ScriptedPythonInterface() = default;
29 
30 protected:
31   template <typename T = StructuredData::ObjectSP>
32   T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {
33     return p.CreateStructuredObject();
34   }
35 
36   template <>
37   Status ExtractValueFromPythonObject<Status>(python::PythonObject &p,
38                                               Status &error) {
39     if (lldb::SBError *sb_error = reinterpret_cast<lldb::SBError *>(
40             LLDBSWIGPython_CastPyObjectToSBError(p.get())))
41       error = m_interpreter.GetStatusFromSBError(*sb_error);
42     else
43       error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status.");
44 
45     return error;
46   }
47 
48   template <>
49   lldb::DataExtractorSP
50   ExtractValueFromPythonObject<lldb::DataExtractorSP>(python::PythonObject &p,
51                                                       Status &error) {
52     lldb::SBData *sb_data = reinterpret_cast<lldb::SBData *>(
53         LLDBSWIGPython_CastPyObjectToSBData(p.get()));
54 
55     if (!sb_data) {
56       error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status.");
57       return nullptr;
58     }
59 
60     return m_interpreter.GetDataExtractorFromSBData(*sb_data);
61   }
62 
63   template <typename T = StructuredData::ObjectSP, typename... Args>
64   T Dispatch(llvm::StringRef method_name, Status &error, Args... args) {
65     using namespace python;
66     using Locker = ScriptInterpreterPythonImpl::Locker;
67 
68     auto error_with_message = [&method_name, &error](llvm::StringRef message) {
69       error.SetErrorStringWithFormatv(
70           "ScriptedPythonInterface::{0} ({1}) ERROR = {2}", __FUNCTION__,
71           method_name, message);
72       return T();
73     };
74 
75     if (!m_object_instance_sp)
76       return error_with_message("Python object ill-formed");
77 
78     Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
79                    Locker::FreeLock);
80 
81     PythonObject implementor(PyRefType::Borrowed,
82                              (PyObject *)m_object_instance_sp->GetValue());
83 
84     if (!implementor.IsAllocated())
85       return error_with_message("Python implementor not allocated.");
86 
87     PythonObject pmeth(
88         PyRefType::Owned,
89         PyObject_GetAttrString(implementor.get(), method_name.str().c_str()));
90 
91     if (PyErr_Occurred())
92       PyErr_Clear();
93 
94     if (!pmeth.IsAllocated())
95       return error_with_message("Python method not allocated.");
96 
97     if (PyCallable_Check(pmeth.get()) == 0) {
98       if (PyErr_Occurred())
99         PyErr_Clear();
100       return error_with_message("Python method not callable.");
101     }
102 
103     if (PyErr_Occurred())
104       PyErr_Clear();
105 
106     // TODO: make `const char *` when removing support for Python 2.
107     char *format = nullptr;
108     std::string format_buffer;
109 
110     if (sizeof...(Args) > 0) {
111       FormatArgs(format_buffer, args...);
112       // TODO: make `const char *` when removing support for Python 2.
113       format = const_cast<char *>(format_buffer.c_str());
114     }
115 
116     // TODO: make `const char *` when removing support for Python 2.
117     PythonObject py_return(
118         PyRefType::Owned,
119         PyObject_CallMethod(implementor.get(),
120                             const_cast<char *>(method_name.data()), format,
121                             args...));
122 
123     if (PyErr_Occurred()) {
124       PyErr_Print();
125       PyErr_Clear();
126       return error_with_message("Python method could not be called.");
127     }
128 
129     if (!py_return.IsAllocated())
130       return error_with_message("Returned object is null.");
131 
132     return ExtractValueFromPythonObject<T>(py_return, error);
133   }
134 
135   Status GetStatusFromMethod(llvm::StringRef method_name);
136 
137   template <typename T, typename... Args>
138   void FormatArgs(std::string &fmt, T arg, Args... args) const {
139     FormatArgs(fmt, arg);
140     FormatArgs(fmt, args...);
141   }
142 
143   template <typename T> void FormatArgs(std::string &fmt, T arg) const {
144     fmt += GetPythonValueFormatString(arg);
145   }
146 
147   void FormatArgs(std::string &fmt) const {}
148 
149   // The lifetime is managed by the ScriptInterpreter
150   ScriptInterpreterPythonImpl &m_interpreter;
151 };
152 } // namespace lldb_private
153 
154 #endif // LLDB_ENABLE_PYTHON
155 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPYTHONINTERFACE_H
156