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