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