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 eUnknownArgumentCount, 40 eInvalidArgumentCount, 41 eValid 42 }; 43 44 struct AbstrackMethodCheckerPayload { 45 46 struct InvalidArgumentCountPayload { 47 InvalidArgumentCountPayload(size_t required, size_t actual) 48 : required_argument_count(required), actual_argument_count(actual) {} 49 50 size_t required_argument_count; 51 size_t actual_argument_count; 52 }; 53 54 AbstractMethodCheckerCases checker_case; 55 std::variant<std::monostate, InvalidArgumentCountPayload> payload; 56 }; 57 58 llvm::Expected<std::map<llvm::StringLiteral, AbstrackMethodCheckerPayload>> 59 CheckAbstractMethodImplementation( 60 const python::PythonDictionary &class_dict) const { 61 62 using namespace python; 63 64 std::map<llvm::StringLiteral, AbstrackMethodCheckerPayload> checker; 65 #define SET_CASE_AND_CONTINUE(method_name, case) \ 66 { \ 67 checker[method_name] = {case, {}}; \ 68 continue; \ 69 } 70 71 for (const AbstractMethodRequirement &requirement : 72 GetAbstractMethodRequirements()) { 73 llvm::StringLiteral method_name = requirement.name; 74 if (!class_dict.HasKey(method_name)) 75 SET_CASE_AND_CONTINUE(method_name, 76 AbstractMethodCheckerCases::eNotImplemented) 77 auto callable_or_err = class_dict.GetItem(method_name); 78 if (!callable_or_err) { 79 llvm::consumeError(callable_or_err.takeError()); 80 SET_CASE_AND_CONTINUE(method_name, 81 AbstractMethodCheckerCases::eNotAllocated) 82 } 83 84 PythonCallable callable = callable_or_err->AsType<PythonCallable>(); 85 if (!callable) 86 SET_CASE_AND_CONTINUE(method_name, 87 AbstractMethodCheckerCases::eNotCallable) 88 89 if (!requirement.min_arg_count) 90 SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eValid) 91 92 auto arg_info_or_err = callable.GetArgInfo(); 93 if (!arg_info_or_err) { 94 llvm::consumeError(arg_info_or_err.takeError()); 95 SET_CASE_AND_CONTINUE(method_name, 96 AbstractMethodCheckerCases::eUnknownArgumentCount) 97 } 98 99 PythonCallable::ArgInfo arg_info = *arg_info_or_err; 100 if (requirement.min_arg_count <= arg_info.max_positional_args) { 101 SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eValid) 102 } else { 103 checker[method_name] = { 104 AbstractMethodCheckerCases::eInvalidArgumentCount, 105 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload( 106 requirement.min_arg_count, arg_info.max_positional_args)}; 107 } 108 } 109 110 #undef SET_CASE_AND_CONTINUE 111 112 return checker; 113 } 114 115 template <typename... Args> 116 llvm::Expected<StructuredData::GenericSP> 117 CreatePluginObject(llvm::StringRef class_name, 118 StructuredData::Generic *script_obj, Args... args) { 119 using namespace python; 120 using Locker = ScriptInterpreterPythonImpl::Locker; 121 122 Log *log = GetLog(LLDBLog::Script); 123 auto create_error = [](llvm::StringLiteral format, auto &&...ts) { 124 return llvm::createStringError( 125 llvm::formatv(format.data(), std::forward<decltype(ts)>(ts)...) 126 .str()); 127 }; 128 129 bool has_class_name = !class_name.empty(); 130 bool has_interpreter_dict = 131 !(llvm::StringRef(m_interpreter.GetDictionaryName()).empty()); 132 if (!has_class_name && !has_interpreter_dict && !script_obj) { 133 if (!has_class_name) 134 return create_error("Missing script class name."); 135 else if (!has_interpreter_dict) 136 return create_error("Invalid script interpreter dictionary."); 137 else 138 return create_error("Missing scripting object."); 139 } 140 141 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, 142 Locker::FreeLock); 143 144 PythonObject result = {}; 145 146 if (script_obj) { 147 result = PythonObject(PyRefType::Borrowed, 148 static_cast<PyObject *>(script_obj->GetValue())); 149 } else { 150 auto dict = 151 PythonModule::MainModule().ResolveName<python::PythonDictionary>( 152 m_interpreter.GetDictionaryName()); 153 if (!dict.IsAllocated()) 154 return create_error("Could not find interpreter dictionary: {0}", 155 m_interpreter.GetDictionaryName()); 156 157 auto init = 158 PythonObject::ResolveNameWithDictionary<python::PythonCallable>( 159 class_name, dict); 160 if (!init.IsAllocated()) 161 return create_error("Could not find script class: {0}", 162 class_name.data()); 163 164 std::tuple<Args...> original_args = std::forward_as_tuple(args...); 165 auto transformed_args = TransformArgs(original_args); 166 167 std::string error_string; 168 llvm::Expected<PythonCallable::ArgInfo> arg_info = init.GetArgInfo(); 169 if (!arg_info) { 170 llvm::handleAllErrors( 171 arg_info.takeError(), 172 [&](PythonException &E) { error_string.append(E.ReadBacktrace()); }, 173 [&](const llvm::ErrorInfoBase &E) { 174 error_string.append(E.message()); 175 }); 176 return llvm::createStringError(llvm::inconvertibleErrorCode(), 177 error_string); 178 } 179 180 llvm::Expected<PythonObject> expected_return_object = 181 create_error("Resulting object is not initialized."); 182 183 std::apply( 184 [&init, &expected_return_object](auto &&...args) { 185 llvm::consumeError(expected_return_object.takeError()); 186 expected_return_object = init(args...); 187 }, 188 transformed_args); 189 190 if (!expected_return_object) 191 return expected_return_object.takeError(); 192 result = expected_return_object.get(); 193 } 194 195 if (!result.IsValid()) 196 return create_error("Resulting object is not a valid Python Object."); 197 if (!result.HasAttribute("__class__")) 198 return create_error("Resulting object doesn't have '__class__' member."); 199 200 PythonObject obj_class = result.GetAttributeValue("__class__"); 201 if (!obj_class.IsValid()) 202 return create_error("Resulting class object is not a valid."); 203 if (!obj_class.HasAttribute("__name__")) 204 return create_error( 205 "Resulting object class doesn't have '__name__' member."); 206 PythonString obj_class_name = 207 obj_class.GetAttributeValue("__name__").AsType<PythonString>(); 208 209 PythonObject object_class_mapping_proxy = 210 obj_class.GetAttributeValue("__dict__"); 211 if (!obj_class.HasAttribute("__dict__")) 212 return create_error( 213 "Resulting object class doesn't have '__dict__' member."); 214 215 PythonCallable dict_converter = PythonModule::BuiltinsModule() 216 .ResolveName("dict") 217 .AsType<PythonCallable>(); 218 if (!dict_converter.IsAllocated()) 219 return create_error( 220 "Python 'builtins' module doesn't have 'dict' class."); 221 222 PythonDictionary object_class_dict = 223 dict_converter(object_class_mapping_proxy).AsType<PythonDictionary>(); 224 if (!object_class_dict.IsAllocated()) 225 return create_error("Coudn't create dictionary from resulting object " 226 "class mapping proxy object."); 227 228 auto checker_or_err = CheckAbstractMethodImplementation(object_class_dict); 229 if (!checker_or_err) 230 return checker_or_err.takeError(); 231 232 llvm::Error abstract_method_errors = llvm::Error::success(); 233 for (const auto &method_checker : *checker_or_err) 234 switch (method_checker.second.checker_case) { 235 case AbstractMethodCheckerCases::eNotImplemented: 236 abstract_method_errors = llvm::joinErrors( 237 std::move(abstract_method_errors), 238 std::move(create_error("Abstract method {0}.{1} not implemented.", 239 obj_class_name.GetString(), 240 method_checker.first))); 241 break; 242 case AbstractMethodCheckerCases::eNotAllocated: 243 abstract_method_errors = llvm::joinErrors( 244 std::move(abstract_method_errors), 245 std::move(create_error("Abstract method {0}.{1} not allocated.", 246 obj_class_name.GetString(), 247 method_checker.first))); 248 break; 249 case AbstractMethodCheckerCases::eNotCallable: 250 abstract_method_errors = llvm::joinErrors( 251 std::move(abstract_method_errors), 252 std::move(create_error("Abstract method {0}.{1} not callable.", 253 obj_class_name.GetString(), 254 method_checker.first))); 255 break; 256 case AbstractMethodCheckerCases::eUnknownArgumentCount: 257 abstract_method_errors = llvm::joinErrors( 258 std::move(abstract_method_errors), 259 std::move(create_error( 260 "Abstract method {0}.{1} has unknown argument count.", 261 obj_class_name.GetString(), method_checker.first))); 262 break; 263 case AbstractMethodCheckerCases::eInvalidArgumentCount: { 264 auto &payload_variant = method_checker.second.payload; 265 if (!std::holds_alternative< 266 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>( 267 payload_variant)) { 268 abstract_method_errors = llvm::joinErrors( 269 std::move(abstract_method_errors), 270 std::move(create_error( 271 "Abstract method {0}.{1} has unexpected argument count.", 272 obj_class_name.GetString(), method_checker.first))); 273 } else { 274 auto payload = std::get< 275 AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>( 276 payload_variant); 277 abstract_method_errors = llvm::joinErrors( 278 std::move(abstract_method_errors), 279 std::move( 280 create_error("Abstract method {0}.{1} has unexpected " 281 "argument count (expected {2} but has {3}).", 282 obj_class_name.GetString(), method_checker.first, 283 payload.required_argument_count, 284 payload.actual_argument_count))); 285 } 286 } break; 287 case AbstractMethodCheckerCases::eValid: 288 LLDB_LOG(log, "Abstract method {0}.{1} implemented & valid.", 289 obj_class_name.GetString(), method_checker.first); 290 break; 291 } 292 293 if (abstract_method_errors) { 294 Status error = Status::FromError(std::move(abstract_method_errors)); 295 LLDB_LOG(log, "Abstract method error in {0}:\n{1}", class_name, 296 error.AsCString()); 297 return error.ToError(); 298 } 299 300 m_object_instance_sp = StructuredData::GenericSP( 301 new StructuredPythonObject(std::move(result))); 302 return m_object_instance_sp; 303 } 304 305 protected: 306 template <typename T = StructuredData::ObjectSP> 307 T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { 308 return p.CreateStructuredObject(); 309 } 310 311 template <typename T = StructuredData::ObjectSP, typename... Args> 312 T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) { 313 using namespace python; 314 using Locker = ScriptInterpreterPythonImpl::Locker; 315 316 std::string caller_signature = 317 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") + 318 llvm::Twine(method_name) + llvm::Twine(")")) 319 .str(); 320 if (!m_object_instance_sp) 321 return ErrorWithMessage<T>(caller_signature, "Python object ill-formed", 322 error); 323 324 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, 325 Locker::FreeLock); 326 327 PythonObject implementor(PyRefType::Borrowed, 328 (PyObject *)m_object_instance_sp->GetValue()); 329 330 if (!implementor.IsAllocated()) 331 return llvm::is_contained(GetAbstractMethods(), method_name) 332 ? ErrorWithMessage<T>(caller_signature, 333 "Python implementor not allocated.", 334 error) 335 : T{}; 336 337 std::tuple<Args...> original_args = std::forward_as_tuple(args...); 338 auto transformed_args = TransformArgs(original_args); 339 340 llvm::Expected<PythonObject> expected_return_object = 341 llvm::make_error<llvm::StringError>("Not initialized.", 342 llvm::inconvertibleErrorCode()); 343 std::apply( 344 [&implementor, &method_name, &expected_return_object](auto &&...args) { 345 llvm::consumeError(expected_return_object.takeError()); 346 expected_return_object = 347 implementor.CallMethod(method_name.data(), args...); 348 }, 349 transformed_args); 350 351 if (llvm::Error e = expected_return_object.takeError()) { 352 error = Status::FromError(std::move(e)); 353 return ErrorWithMessage<T>(caller_signature, 354 "Python method could not be called.", error); 355 } 356 357 PythonObject py_return = std::move(expected_return_object.get()); 358 359 // Now that we called the python method with the transformed arguments, 360 // we need to interate again over both the original and transformed 361 // parameter pack, and transform back the parameter that were passed in 362 // the original parameter pack as references or pointers. 363 if (sizeof...(Args) > 0) 364 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) 365 return ErrorWithMessage<T>( 366 caller_signature, 367 "Couldn't re-assign reference and pointer arguments.", error); 368 369 if (!py_return.IsAllocated()) 370 return {}; 371 return ExtractValueFromPythonObject<T>(py_return, error); 372 } 373 374 template <typename... Args> 375 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) { 376 Status error; 377 Dispatch<Status>(method_name, error, std::forward<Args>(args)...); 378 379 return error; 380 } 381 382 template <typename T> T Transform(T object) { 383 // No Transformation for generic usage 384 return {object}; 385 } 386 387 python::PythonObject Transform(bool arg) { 388 // Boolean arguments need to be turned into python objects. 389 return python::PythonBoolean(arg); 390 } 391 392 python::PythonObject Transform(const Status &arg) { 393 return python::SWIGBridge::ToSWIGWrapper(arg.Clone()); 394 } 395 396 python::PythonObject Transform(Status &&arg) { 397 return python::SWIGBridge::ToSWIGWrapper(std::move(arg)); 398 } 399 400 python::PythonObject Transform(const StructuredDataImpl &arg) { 401 return python::SWIGBridge::ToSWIGWrapper(arg); 402 } 403 404 python::PythonObject Transform(lldb::ExecutionContextRefSP arg) { 405 return python::SWIGBridge::ToSWIGWrapper(arg); 406 } 407 408 python::PythonObject Transform(lldb::ProcessSP arg) { 409 return python::SWIGBridge::ToSWIGWrapper(arg); 410 } 411 412 python::PythonObject Transform(lldb::ThreadPlanSP arg) { 413 return python::SWIGBridge::ToSWIGWrapper(arg); 414 } 415 416 python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) { 417 return python::SWIGBridge::ToSWIGWrapper(arg); 418 } 419 420 python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) { 421 return python::SWIGBridge::ToSWIGWrapper(arg); 422 } 423 424 python::PythonObject Transform(Event *arg) { 425 return python::SWIGBridge::ToSWIGWrapper(arg); 426 } 427 428 python::PythonObject Transform(lldb::StreamSP arg) { 429 return python::SWIGBridge::ToSWIGWrapper(arg.get()); 430 } 431 432 python::PythonObject Transform(lldb::DataExtractorSP arg) { 433 return python::SWIGBridge::ToSWIGWrapper(arg); 434 } 435 436 template <typename T, typename U> 437 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { 438 // If U is not a PythonObject, don't touch it! 439 return; 440 } 441 442 template <typename T> 443 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, 444 Status &error) { 445 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error); 446 } 447 448 void ReverseTransform(bool &original_arg, 449 python::PythonObject transformed_arg, Status &error) { 450 python::PythonBoolean boolean_arg = python::PythonBoolean( 451 python::PyRefType::Borrowed, transformed_arg.get()); 452 if (boolean_arg.IsValid()) 453 original_arg = boolean_arg.GetValue(); 454 else 455 error = Status::FromErrorStringWithFormatv( 456 "{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION); 457 } 458 459 template <std::size_t... I, typename... Args> 460 auto TransformTuple(const std::tuple<Args...> &args, 461 std::index_sequence<I...>) { 462 return std::make_tuple(Transform(std::get<I>(args))...); 463 } 464 465 // This will iterate over the Dispatch parameter pack and replace in-place 466 // every `lldb_private` argument that has a SB counterpart. 467 template <typename... Args> 468 auto TransformArgs(const std::tuple<Args...> &args) { 469 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>()); 470 } 471 472 template <typename T, typename U> 473 void TransformBack(T &original_arg, U transformed_arg, Status &error) { 474 ReverseTransform(original_arg, transformed_arg, error); 475 } 476 477 template <std::size_t... I, typename... Ts, typename... Us> 478 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 479 std::tuple<Us...> &transformed_args, 480 std::index_sequence<I...>) { 481 Status error; 482 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args), 483 error), 484 ...); 485 return error.Success(); 486 } 487 488 template <typename... Ts, typename... Us> 489 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 490 std::tuple<Us...> &transformed_args) { 491 if (sizeof...(Ts) != sizeof...(Us)) 492 return false; 493 494 return ReassignPtrsOrRefsArgs(original_args, transformed_args, 495 std::make_index_sequence<sizeof...(Ts)>()); 496 } 497 498 template <typename T, typename... Args> 499 void FormatArgs(std::string &fmt, T arg, Args... args) const { 500 FormatArgs(fmt, arg); 501 FormatArgs(fmt, args...); 502 } 503 504 template <typename T> void FormatArgs(std::string &fmt, T arg) const { 505 fmt += python::PythonFormat<T>::format; 506 } 507 508 void FormatArgs(std::string &fmt) const {} 509 510 // The lifetime is managed by the ScriptInterpreter 511 ScriptInterpreterPythonImpl &m_interpreter; 512 }; 513 514 template <> 515 StructuredData::ArraySP 516 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( 517 python::PythonObject &p, Status &error); 518 519 template <> 520 StructuredData::DictionarySP 521 ScriptedPythonInterface::ExtractValueFromPythonObject< 522 StructuredData::DictionarySP>(python::PythonObject &p, Status &error); 523 524 template <> 525 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( 526 python::PythonObject &p, Status &error); 527 528 template <> 529 Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>( 530 python::PythonObject &p, Status &error); 531 532 template <> 533 lldb::StreamSP 534 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>( 535 python::PythonObject &p, Status &error); 536 537 template <> 538 lldb::BreakpointSP 539 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>( 540 python::PythonObject &p, Status &error); 541 542 template <> 543 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 544 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error); 545 546 template <> 547 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 548 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error); 549 550 template <> 551 lldb::DataExtractorSP 552 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( 553 python::PythonObject &p, Status &error); 554 555 template <> 556 std::optional<MemoryRegionInfo> 557 ScriptedPythonInterface::ExtractValueFromPythonObject< 558 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error); 559 560 } // namespace lldb_private 561 562 #endif // LLDB_ENABLE_PYTHON 563 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H 564