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_INTERFACES_SCRIPTEDPYTHONINTERFACE_H 10 #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H 11 12 #if LLDB_ENABLE_PYTHON 13 14 #include <optional> 15 #include <sstream> 16 #include <tuple> 17 #include <type_traits> 18 #include <utility> 19 20 #include "lldb/Host/Config.h" 21 #include "lldb/Interpreter/Interfaces/ScriptedInterface.h" 22 #include "lldb/Utility/DataBufferHeap.h" 23 24 #include "../PythonDataObjects.h" 25 #include "../SWIGPythonBridge.h" 26 #include "../ScriptInterpreterPythonImpl.h" 27 28 namespace lldb_private { 29 class ScriptInterpreterPythonImpl; 30 class ScriptedPythonInterface : virtual public ScriptedInterface { 31 public: 32 ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter); 33 ~ScriptedPythonInterface() override = default; 34 35 enum class AbstractMethodCheckerCases { 36 eNotImplemented, 37 eNotAllocated, 38 eNotCallable, 39 eValid 40 }; 41 42 llvm::Expected<std::map<llvm::StringLiteral, AbstractMethodCheckerCases>> CheckAbstractMethodImplementation(const python::PythonDictionary & class_dict)43 CheckAbstractMethodImplementation( 44 const python::PythonDictionary &class_dict) const { 45 46 using namespace python; 47 48 std::map<llvm::StringLiteral, AbstractMethodCheckerCases> checker; 49 #define SET_ERROR_AND_CONTINUE(method_name, error) \ 50 { \ 51 checker[method_name] = error; \ 52 continue; \ 53 } 54 55 for (const llvm::StringLiteral &method_name : GetAbstractMethods()) { 56 if (!class_dict.HasKey(method_name)) 57 SET_ERROR_AND_CONTINUE(method_name, 58 AbstractMethodCheckerCases::eNotImplemented) 59 auto callable_or_err = class_dict.GetItem(method_name); 60 if (!callable_or_err) 61 SET_ERROR_AND_CONTINUE(method_name, 62 AbstractMethodCheckerCases::eNotAllocated) 63 if (!PythonCallable::Check(callable_or_err.get().get())) 64 SET_ERROR_AND_CONTINUE(method_name, 65 AbstractMethodCheckerCases::eNotCallable) 66 checker[method_name] = AbstractMethodCheckerCases::eValid; 67 } 68 69 #undef HANDLE_ERROR 70 71 return checker; 72 } 73 74 template <typename... Args> 75 llvm::Expected<StructuredData::GenericSP> CreatePluginObject(llvm::StringRef class_name,StructuredData::Generic * script_obj,Args...args)76 CreatePluginObject(llvm::StringRef class_name, 77 StructuredData::Generic *script_obj, Args... args) { 78 using namespace python; 79 using Locker = ScriptInterpreterPythonImpl::Locker; 80 81 auto create_error = [](std::string message) { 82 return llvm::createStringError(llvm::inconvertibleErrorCode(), message); 83 }; 84 85 bool has_class_name = !class_name.empty(); 86 bool has_interpreter_dict = 87 !(llvm::StringRef(m_interpreter.GetDictionaryName()).empty()); 88 if (!has_class_name && !has_interpreter_dict && !script_obj) { 89 if (!has_class_name) 90 return create_error("Missing script class name."); 91 else if (!has_interpreter_dict) 92 return create_error("Invalid script interpreter dictionary."); 93 else 94 return create_error("Missing scripting object."); 95 } 96 97 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, 98 Locker::FreeLock); 99 100 PythonObject result = {}; 101 102 if (script_obj) { 103 result = PythonObject(PyRefType::Borrowed, 104 static_cast<PyObject *>(script_obj->GetValue())); 105 } else { 106 auto dict = 107 PythonModule::MainModule().ResolveName<python::PythonDictionary>( 108 m_interpreter.GetDictionaryName()); 109 if (!dict.IsAllocated()) 110 return create_error( 111 llvm::formatv("Could not find interpreter dictionary: %s", 112 m_interpreter.GetDictionaryName())); 113 114 auto init = 115 PythonObject::ResolveNameWithDictionary<python::PythonCallable>( 116 class_name, dict); 117 if (!init.IsAllocated()) 118 return create_error(llvm::formatv("Could not find script class: %s", 119 class_name.data())); 120 121 std::tuple<Args...> original_args = std::forward_as_tuple(args...); 122 auto transformed_args = TransformArgs(original_args); 123 124 std::string error_string; 125 llvm::Expected<PythonCallable::ArgInfo> arg_info = init.GetArgInfo(); 126 if (!arg_info) { 127 llvm::handleAllErrors( 128 arg_info.takeError(), 129 [&](PythonException &E) { error_string.append(E.ReadBacktrace()); }, 130 [&](const llvm::ErrorInfoBase &E) { 131 error_string.append(E.message()); 132 }); 133 return llvm::createStringError(llvm::inconvertibleErrorCode(), 134 error_string); 135 } 136 137 llvm::Expected<PythonObject> expected_return_object = 138 create_error("Resulting object is not initialized."); 139 140 std::apply( 141 [&init, &expected_return_object](auto &&...args) { 142 llvm::consumeError(expected_return_object.takeError()); 143 expected_return_object = init(args...); 144 }, 145 transformed_args); 146 147 if (!expected_return_object) 148 return expected_return_object.takeError(); 149 result = expected_return_object.get(); 150 } 151 152 if (!result.IsValid()) 153 return create_error("Resulting object is not a valid Python Object."); 154 if (!result.HasAttribute("__class__")) 155 return create_error("Resulting object doesn't have '__class__' member."); 156 157 PythonObject obj_class = result.GetAttributeValue("__class__"); 158 if (!obj_class.IsValid()) 159 return create_error("Resulting class object is not a valid."); 160 if (!obj_class.HasAttribute("__name__")) 161 return create_error( 162 "Resulting object class doesn't have '__name__' member."); 163 PythonString obj_class_name = 164 obj_class.GetAttributeValue("__name__").AsType<PythonString>(); 165 166 PythonObject object_class_mapping_proxy = 167 obj_class.GetAttributeValue("__dict__"); 168 if (!obj_class.HasAttribute("__dict__")) 169 return create_error( 170 "Resulting object class doesn't have '__dict__' member."); 171 172 PythonCallable dict_converter = PythonModule::BuiltinsModule() 173 .ResolveName("dict") 174 .AsType<PythonCallable>(); 175 if (!dict_converter.IsAllocated()) 176 return create_error( 177 "Python 'builtins' module doesn't have 'dict' class."); 178 179 PythonDictionary object_class_dict = 180 dict_converter(object_class_mapping_proxy).AsType<PythonDictionary>(); 181 if (!object_class_dict.IsAllocated()) 182 return create_error("Coudn't create dictionary from resulting object " 183 "class mapping proxy object."); 184 185 auto checker_or_err = CheckAbstractMethodImplementation(object_class_dict); 186 if (!checker_or_err) 187 return checker_or_err.takeError(); 188 189 for (const auto &method_checker : *checker_or_err) 190 switch (method_checker.second) { 191 case AbstractMethodCheckerCases::eNotImplemented: 192 LLDB_LOG(GetLog(LLDBLog::Script), 193 "Abstract method {0}.{1} not implemented.", 194 obj_class_name.GetString(), method_checker.first); 195 break; 196 case AbstractMethodCheckerCases::eNotAllocated: 197 LLDB_LOG(GetLog(LLDBLog::Script), 198 "Abstract method {0}.{1} not allocated.", 199 obj_class_name.GetString(), method_checker.first); 200 break; 201 case AbstractMethodCheckerCases::eNotCallable: 202 LLDB_LOG(GetLog(LLDBLog::Script), 203 "Abstract method {0}.{1} not callable.", 204 obj_class_name.GetString(), method_checker.first); 205 break; 206 case AbstractMethodCheckerCases::eValid: 207 LLDB_LOG(GetLog(LLDBLog::Script), 208 "Abstract method {0}.{1} implemented & valid.", 209 obj_class_name.GetString(), method_checker.first); 210 break; 211 } 212 213 for (const auto &method_checker : *checker_or_err) 214 if (method_checker.second != AbstractMethodCheckerCases::eValid) 215 return create_error( 216 llvm::formatv("Abstract method {0}.{1} missing. Enable lldb " 217 "script log for more details.", 218 obj_class_name.GetString(), method_checker.first)); 219 220 m_object_instance_sp = StructuredData::GenericSP( 221 new StructuredPythonObject(std::move(result))); 222 return m_object_instance_sp; 223 } 224 225 protected: 226 template <typename T = StructuredData::ObjectSP> ExtractValueFromPythonObject(python::PythonObject & p,Status & error)227 T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { 228 return p.CreateStructuredObject(); 229 } 230 231 template <typename T = StructuredData::ObjectSP, typename... Args> Dispatch(llvm::StringRef method_name,Status & error,Args &&...args)232 T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) { 233 using namespace python; 234 using Locker = ScriptInterpreterPythonImpl::Locker; 235 236 std::string caller_signature = 237 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") + 238 llvm::Twine(method_name) + llvm::Twine(")")) 239 .str(); 240 if (!m_object_instance_sp) 241 return ErrorWithMessage<T>(caller_signature, "Python object ill-formed", 242 error); 243 244 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, 245 Locker::FreeLock); 246 247 PythonObject implementor(PyRefType::Borrowed, 248 (PyObject *)m_object_instance_sp->GetValue()); 249 250 if (!implementor.IsAllocated()) 251 return ErrorWithMessage<T>(caller_signature, 252 "Python implementor not allocated.", error); 253 254 std::tuple<Args...> original_args = std::forward_as_tuple(args...); 255 auto transformed_args = TransformArgs(original_args); 256 257 llvm::Expected<PythonObject> expected_return_object = 258 llvm::make_error<llvm::StringError>("Not initialized.", 259 llvm::inconvertibleErrorCode()); 260 std::apply( 261 [&implementor, &method_name, &expected_return_object](auto &&...args) { 262 llvm::consumeError(expected_return_object.takeError()); 263 expected_return_object = 264 implementor.CallMethod(method_name.data(), args...); 265 }, 266 transformed_args); 267 268 if (llvm::Error e = expected_return_object.takeError()) { 269 error.SetErrorString(llvm::toString(std::move(e)).c_str()); 270 return ErrorWithMessage<T>(caller_signature, 271 "Python method could not be called.", error); 272 } 273 274 PythonObject py_return = std::move(expected_return_object.get()); 275 276 // Now that we called the python method with the transformed arguments, 277 // we need to interate again over both the original and transformed 278 // parameter pack, and transform back the parameter that were passed in 279 // the original parameter pack as references or pointers. 280 if (sizeof...(Args) > 0) 281 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) 282 return ErrorWithMessage<T>( 283 caller_signature, 284 "Couldn't re-assign reference and pointer arguments.", error); 285 286 if (!py_return.IsAllocated()) 287 return {}; 288 return ExtractValueFromPythonObject<T>(py_return, error); 289 } 290 291 template <typename... Args> GetStatusFromMethod(llvm::StringRef method_name,Args &&...args)292 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) { 293 Status error; 294 Dispatch<Status>(method_name, error, std::forward<Args>(args)...); 295 296 return error; 297 } 298 Transform(T object)299 template <typename T> T Transform(T object) { 300 // No Transformation for generic usage 301 return {object}; 302 } 303 Transform(bool arg)304 python::PythonObject Transform(bool arg) { 305 // Boolean arguments need to be turned into python objects. 306 return python::PythonBoolean(arg); 307 } 308 Transform(Status arg)309 python::PythonObject Transform(Status arg) { 310 return python::SWIGBridge::ToSWIGWrapper(arg); 311 } 312 Transform(const StructuredDataImpl & arg)313 python::PythonObject Transform(const StructuredDataImpl &arg) { 314 return python::SWIGBridge::ToSWIGWrapper(arg); 315 } 316 Transform(lldb::ExecutionContextRefSP arg)317 python::PythonObject Transform(lldb::ExecutionContextRefSP arg) { 318 return python::SWIGBridge::ToSWIGWrapper(arg); 319 } 320 Transform(lldb::ProcessSP arg)321 python::PythonObject Transform(lldb::ProcessSP arg) { 322 return python::SWIGBridge::ToSWIGWrapper(arg); 323 } 324 Transform(lldb::ProcessAttachInfoSP arg)325 python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) { 326 return python::SWIGBridge::ToSWIGWrapper(arg); 327 } 328 Transform(lldb::ProcessLaunchInfoSP arg)329 python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) { 330 return python::SWIGBridge::ToSWIGWrapper(arg); 331 } 332 Transform(lldb::DataExtractorSP arg)333 python::PythonObject Transform(lldb::DataExtractorSP arg) { 334 return python::SWIGBridge::ToSWIGWrapper(arg); 335 } 336 337 template <typename T, typename U> ReverseTransform(T & original_arg,U transformed_arg,Status & error)338 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { 339 // If U is not a PythonObject, don't touch it! 340 return; 341 } 342 343 template <typename T> ReverseTransform(T & original_arg,python::PythonObject transformed_arg,Status & error)344 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, 345 Status &error) { 346 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error); 347 } 348 ReverseTransform(bool & original_arg,python::PythonObject transformed_arg,Status & error)349 void ReverseTransform(bool &original_arg, 350 python::PythonObject transformed_arg, Status &error) { 351 python::PythonBoolean boolean_arg = python::PythonBoolean( 352 python::PyRefType::Borrowed, transformed_arg.get()); 353 if (boolean_arg.IsValid()) 354 original_arg = boolean_arg.GetValue(); 355 else 356 error.SetErrorString( 357 llvm::formatv("{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION) 358 .str()); 359 } 360 361 template <std::size_t... I, typename... Args> TransformTuple(const std::tuple<Args...> & args,std::index_sequence<I...>)362 auto TransformTuple(const std::tuple<Args...> &args, 363 std::index_sequence<I...>) { 364 return std::make_tuple(Transform(std::get<I>(args))...); 365 } 366 367 // This will iterate over the Dispatch parameter pack and replace in-place 368 // every `lldb_private` argument that has a SB counterpart. 369 template <typename... Args> TransformArgs(const std::tuple<Args...> & args)370 auto TransformArgs(const std::tuple<Args...> &args) { 371 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>()); 372 } 373 374 template <typename T, typename U> TransformBack(T & original_arg,U transformed_arg,Status & error)375 void TransformBack(T &original_arg, U transformed_arg, Status &error) { 376 ReverseTransform(original_arg, transformed_arg, error); 377 } 378 379 template <std::size_t... I, typename... Ts, typename... Us> ReassignPtrsOrRefsArgs(std::tuple<Ts...> & original_args,std::tuple<Us...> & transformed_args,std::index_sequence<I...>)380 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 381 std::tuple<Us...> &transformed_args, 382 std::index_sequence<I...>) { 383 Status error; 384 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args), 385 error), 386 ...); 387 return error.Success(); 388 } 389 390 template <typename... Ts, typename... Us> ReassignPtrsOrRefsArgs(std::tuple<Ts...> & original_args,std::tuple<Us...> & transformed_args)391 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 392 std::tuple<Us...> &transformed_args) { 393 if (sizeof...(Ts) != sizeof...(Us)) 394 return false; 395 396 return ReassignPtrsOrRefsArgs(original_args, transformed_args, 397 std::make_index_sequence<sizeof...(Ts)>()); 398 } 399 400 template <typename T, typename... Args> FormatArgs(std::string & fmt,T arg,Args...args)401 void FormatArgs(std::string &fmt, T arg, Args... args) const { 402 FormatArgs(fmt, arg); 403 FormatArgs(fmt, args...); 404 } 405 FormatArgs(std::string & fmt,T arg)406 template <typename T> void FormatArgs(std::string &fmt, T arg) const { 407 fmt += python::PythonFormat<T>::format; 408 } 409 FormatArgs(std::string & fmt)410 void FormatArgs(std::string &fmt) const {} 411 412 // The lifetime is managed by the ScriptInterpreter 413 ScriptInterpreterPythonImpl &m_interpreter; 414 }; 415 416 template <> 417 StructuredData::ArraySP 418 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( 419 python::PythonObject &p, Status &error); 420 421 template <> 422 StructuredData::DictionarySP 423 ScriptedPythonInterface::ExtractValueFromPythonObject< 424 StructuredData::DictionarySP>(python::PythonObject &p, Status &error); 425 426 template <> 427 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( 428 python::PythonObject &p, Status &error); 429 430 template <> 431 lldb::BreakpointSP 432 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>( 433 python::PythonObject &p, Status &error); 434 435 template <> 436 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 437 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error); 438 439 template <> 440 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 441 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error); 442 443 template <> 444 lldb::DataExtractorSP 445 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( 446 python::PythonObject &p, Status &error); 447 448 template <> 449 std::optional<MemoryRegionInfo> 450 ScriptedPythonInterface::ExtractValueFromPythonObject< 451 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error); 452 453 } // namespace lldb_private 454 455 #endif // LLDB_ENABLE_PYTHON 456 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H 457