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.SetErrorString(llvm::toString(std::move(e)).c_str()); 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(Status arg) { 313 return python::SWIGBridge::ToSWIGWrapper(arg); 314 } 315 316 python::PythonObject Transform(const StructuredDataImpl &arg) { 317 return python::SWIGBridge::ToSWIGWrapper(arg); 318 } 319 320 python::PythonObject Transform(lldb::ExecutionContextRefSP arg) { 321 return python::SWIGBridge::ToSWIGWrapper(arg); 322 } 323 324 python::PythonObject Transform(lldb::ProcessSP arg) { 325 return python::SWIGBridge::ToSWIGWrapper(arg); 326 } 327 328 python::PythonObject Transform(lldb::ThreadPlanSP arg) { 329 return python::SWIGBridge::ToSWIGWrapper(arg); 330 } 331 332 python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) { 333 return python::SWIGBridge::ToSWIGWrapper(arg); 334 } 335 336 python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) { 337 return python::SWIGBridge::ToSWIGWrapper(arg); 338 } 339 340 python::PythonObject Transform(Event *arg) { 341 return python::SWIGBridge::ToSWIGWrapper(arg); 342 } 343 344 python::PythonObject Transform(lldb::StreamSP arg) { 345 return python::SWIGBridge::ToSWIGWrapper(arg.get()); 346 } 347 348 python::PythonObject Transform(lldb::DataExtractorSP arg) { 349 return python::SWIGBridge::ToSWIGWrapper(arg); 350 } 351 352 template <typename T, typename U> 353 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { 354 // If U is not a PythonObject, don't touch it! 355 return; 356 } 357 358 template <typename T> 359 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg, 360 Status &error) { 361 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error); 362 } 363 364 void ReverseTransform(bool &original_arg, 365 python::PythonObject transformed_arg, Status &error) { 366 python::PythonBoolean boolean_arg = python::PythonBoolean( 367 python::PyRefType::Borrowed, transformed_arg.get()); 368 if (boolean_arg.IsValid()) 369 original_arg = boolean_arg.GetValue(); 370 else 371 error.SetErrorString( 372 llvm::formatv("{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION) 373 .str()); 374 } 375 376 template <std::size_t... I, typename... Args> 377 auto TransformTuple(const std::tuple<Args...> &args, 378 std::index_sequence<I...>) { 379 return std::make_tuple(Transform(std::get<I>(args))...); 380 } 381 382 // This will iterate over the Dispatch parameter pack and replace in-place 383 // every `lldb_private` argument that has a SB counterpart. 384 template <typename... Args> 385 auto TransformArgs(const std::tuple<Args...> &args) { 386 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>()); 387 } 388 389 template <typename T, typename U> 390 void TransformBack(T &original_arg, U transformed_arg, Status &error) { 391 ReverseTransform(original_arg, transformed_arg, error); 392 } 393 394 template <std::size_t... I, typename... Ts, typename... Us> 395 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 396 std::tuple<Us...> &transformed_args, 397 std::index_sequence<I...>) { 398 Status error; 399 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args), 400 error), 401 ...); 402 return error.Success(); 403 } 404 405 template <typename... Ts, typename... Us> 406 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args, 407 std::tuple<Us...> &transformed_args) { 408 if (sizeof...(Ts) != sizeof...(Us)) 409 return false; 410 411 return ReassignPtrsOrRefsArgs(original_args, transformed_args, 412 std::make_index_sequence<sizeof...(Ts)>()); 413 } 414 415 template <typename T, typename... Args> 416 void FormatArgs(std::string &fmt, T arg, Args... args) const { 417 FormatArgs(fmt, arg); 418 FormatArgs(fmt, args...); 419 } 420 421 template <typename T> void FormatArgs(std::string &fmt, T arg) const { 422 fmt += python::PythonFormat<T>::format; 423 } 424 425 void FormatArgs(std::string &fmt) const {} 426 427 // The lifetime is managed by the ScriptInterpreter 428 ScriptInterpreterPythonImpl &m_interpreter; 429 }; 430 431 template <> 432 StructuredData::ArraySP 433 ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( 434 python::PythonObject &p, Status &error); 435 436 template <> 437 StructuredData::DictionarySP 438 ScriptedPythonInterface::ExtractValueFromPythonObject< 439 StructuredData::DictionarySP>(python::PythonObject &p, Status &error); 440 441 template <> 442 Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( 443 python::PythonObject &p, Status &error); 444 445 template <> 446 Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>( 447 python::PythonObject &p, Status &error); 448 449 template <> 450 lldb::StreamSP 451 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>( 452 python::PythonObject &p, Status &error); 453 454 template <> 455 lldb::BreakpointSP 456 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>( 457 python::PythonObject &p, Status &error); 458 459 template <> 460 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 461 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error); 462 463 template <> 464 lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< 465 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error); 466 467 template <> 468 lldb::DataExtractorSP 469 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( 470 python::PythonObject &p, Status &error); 471 472 template <> 473 std::optional<MemoryRegionInfo> 474 ScriptedPythonInterface::ExtractValueFromPythonObject< 475 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error); 476 477 } // namespace lldb_private 478 479 #endif // LLDB_ENABLE_PYTHON 480 #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H 481