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 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: {0}", 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> 227 T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { 228 return p.CreateStructuredObject(); 229 } 230 231 template <typename T = StructuredData::ObjectSP, typename... 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 llvm::is_contained(GetAbstractMethods(), method_name) 252 ? ErrorWithMessage<T>(caller_signature, 253 "Python implementor not allocated.", 254 error) 255 : T{}; 256 257 std::tuple<Args...> original_args = std::forward_as_tuple(args...); 258 auto transformed_args = TransformArgs(original_args); 259 260 llvm::Expected<PythonObject> expected_return_object = 261 llvm::make_error<llvm::StringError>("Not initialized.", 262 llvm::inconvertibleErrorCode()); 263 std::apply( 264 [&implementor, &method_name, &expected_return_object](auto &&...args) { 265 llvm::consumeError(expected_return_object.takeError()); 266 expected_return_object = 267 implementor.CallMethod(method_name.data(), args...); 268 }, 269 transformed_args); 270 271 if (llvm::Error e = expected_return_object.takeError()) { 272 error = Status::FromError(std::move(e)); 273 return ErrorWithMessage<T>(caller_signature, 274 "Python method could not be called.", error); 275 } 276 277 PythonObject py_return = std::move(expected_return_object.get()); 278 279 // Now that we called the python method with the transformed arguments, 280 // we need to interate again over both the original and transformed 281 // parameter pack, and transform back the parameter that were passed in 282 // the original parameter pack as references or pointers. 283 if (sizeof...(Args) > 0) 284 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) 285 return ErrorWithMessage<T>( 286 caller_signature, 287 "Couldn't re-assign reference and pointer arguments.", error); 288 289 if (!py_return.IsAllocated()) 290 return {}; 291 return ExtractValueFromPythonObject<T>(py_return, error); 292 } 293 294 template <typename... Args> 295 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) { 296 Status error; 297 Dispatch<Status>(method_name, error, std::forward<Args>(args)...); 298 299 return error; 300 } 301 302 template <typename T> T Transform(T object) { 303 // No Transformation for generic usage 304 return {object}; 305 } 306 307 python::PythonObject Transform(bool arg) { 308 // Boolean arguments need to be turned into python objects. 309 return python::PythonBoolean(arg); 310 } 311 312 python::PythonObject Transform(const Status &arg) { 313 return python::SWIGBridge::ToSWIGWrapper(arg.Clone()); 314 } 315 316 python::PythonObject Transform(Status &&arg) { 317 return python::SWIGBridge::ToSWIGWrapper(std::move(arg)); 318 } 319 320 python::PythonObject Transform(const StructuredDataImpl &arg) { 321 return python::SWIGBridge::ToSWIGWrapper(arg); 322 } 323 324 python::PythonObject Transform(lldb::ExecutionContextRefSP arg) { 325 return python::SWIGBridge::ToSWIGWrapper(arg); 326 } 327 328 python::PythonObject Transform(lldb::ProcessSP arg) { 329 return python::SWIGBridge::ToSWIGWrapper(arg); 330 } 331 332 python::PythonObject Transform(lldb::ThreadPlanSP arg) { 333 return python::SWIGBridge::ToSWIGWrapper(arg); 334 } 335 336 python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) { 337 return python::SWIGBridge::ToSWIGWrapper(arg); 338 } 339 340 python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) { 341 return python::SWIGBridge::ToSWIGWrapper(arg); 342 } 343 344 python::PythonObject Transform(Event *arg) { 345 return python::SWIGBridge::ToSWIGWrapper(arg); 346 } 347 348 python::PythonObject Transform(lldb::StreamSP arg) { 349 return python::SWIGBridge::ToSWIGWrapper(arg.get()); 350 } 351 352 python::PythonObject Transform(lldb::DataExtractorSP arg) { 353 return python::SWIGBridge::ToSWIGWrapper(arg); 354 } 355 356 template <typename T, typename U> 357 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { 358 // If U is not a PythonObject, don't touch it! 359 return; 360 } 361 362 template <typename T> 363 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, 364 Status &error) { 365 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error); 366 } 367 368 void ReverseTransform(bool &original_arg, 369 python::PythonObject transformed_arg, Status &error) { 370 python::PythonBoolean boolean_arg = python::PythonBoolean( 371 python::PyRefType::Borrowed, transformed_arg.get()); 372 if (boolean_arg.IsValid()) 373 original_arg = boolean_arg.GetValue(); 374 else 375 error = Status::FromErrorStringWithFormatv( 376 "{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION); 377 } 378 379 template <std::size_t... I, typename... Args> 380 auto TransformTuple(const std::tuple<Args...> &args, 381 std::index_sequence<I...>) { 382 return std::make_tuple(Transform(std::get<I>(args))...); 383 } 384 385 // This will iterate over the Dispatch parameter pack and replace in-place 386 // every `lldb_private` argument that has a SB counterpart. 387 template <typename... Args> 388 auto TransformArgs(const std::tuple<Args...> &args) { 389 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>()); 390 } 391 392 template <typename T, typename U> 393 void TransformBack(T &original_arg, U transformed_arg, Status &error) { 394 ReverseTransform(original_arg, transformed_arg, error); 395 } 396 397 template <std::size_t... I, typename... Ts, typename... Us> 398 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 399 std::tuple<Us...> &transformed_args, 400 std::index_sequence<I...>) { 401 Status error; 402 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args), 403 error), 404 ...); 405 return error.Success(); 406 } 407 408 template <typename... Ts, typename... Us> 409 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 410 std::tuple<Us...> &transformed_args) { 411 if (sizeof...(Ts) != sizeof...(Us)) 412 return false; 413 414 return ReassignPtrsOrRefsArgs(original_args, transformed_args, 415 std::make_index_sequence<sizeof...(Ts)>()); 416 } 417 418 template <typename T, typename... Args> 419 void FormatArgs(std::string &fmt, T arg, Args... args) const { 420 FormatArgs(fmt, arg); 421 FormatArgs(fmt, args...); 422 } 423 424 template <typename T> void FormatArgs(std::string &fmt, T arg) const { 425 fmt += python::PythonFormat<T>::format; 426 } 427 428 void FormatArgs(std::string &fmt) const {} 429 430 // The lifetime is managed by the ScriptInterpreter 431 ScriptInterpreterPythonImpl &m_interpreter; 432 }; 433 434 template <> 435 StructuredData::ArraySP 436 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( 437 python::PythonObject &p, Status &error); 438 439 template <> 440 StructuredData::DictionarySP 441 ScriptedPythonInterface::ExtractValueFromPythonObject< 442 StructuredData::DictionarySP>(python::PythonObject &p, Status &error); 443 444 template <> 445 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( 446 python::PythonObject &p, Status &error); 447 448 template <> 449 Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>( 450 python::PythonObject &p, Status &error); 451 452 template <> 453 lldb::StreamSP 454 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>( 455 python::PythonObject &p, Status &error); 456 457 template <> 458 lldb::BreakpointSP 459 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>( 460 python::PythonObject &p, Status &error); 461 462 template <> 463 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 464 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error); 465 466 template <> 467 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 468 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error); 469 470 template <> 471 lldb::DataExtractorSP 472 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( 473 python::PythonObject &p, Status &error); 474 475 template <> 476 std::optional<MemoryRegionInfo> 477 ScriptedPythonInterface::ExtractValueFromPythonObject< 478 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error); 479 480 } // namespace lldb_private 481 482 #endif // LLDB_ENABLE_PYTHON 483 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H 484