654 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H10#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H11 12#if LLDB_ENABLE_PYTHON13 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 28namespace lldb_private {29class ScriptInterpreterPythonImpl;30class ScriptedPythonInterface : virtual public ScriptedInterface {31public:32 ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter);33 ~ScriptedPythonInterface() override = default;34 35 enum class AbstractMethodCheckerCases {36 eNotImplemented,37 eNotAllocated,38 eNotCallable,39 eUnknownArgumentCount,40 eInvalidArgumentCount,41 eValid42 };43 44 struct AbstractMethodCheckerPayload {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, AbstractMethodCheckerPayload>>59 CheckAbstractMethodImplementation(60 const python::PythonDictionary &class_dict) const {61 62 using namespace python;63 64 std::map<llvm::StringLiteral, AbstractMethodCheckerPayload> 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 llvm::Expected<PythonObject> callable_or_err =78 class_dict.GetItem(method_name);79 if (!callable_or_err) {80 llvm::consumeError(callable_or_err.takeError());81 SET_CASE_AND_CONTINUE(method_name,82 AbstractMethodCheckerCases::eNotAllocated)83 }84 85 PythonCallable callable = callable_or_err->AsType<PythonCallable>();86 if (!callable)87 SET_CASE_AND_CONTINUE(method_name,88 AbstractMethodCheckerCases::eNotCallable)89 90 if (!requirement.min_arg_count)91 SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eValid)92 93 auto arg_info_or_err = callable.GetArgInfo();94 if (!arg_info_or_err) {95 llvm::consumeError(arg_info_or_err.takeError());96 SET_CASE_AND_CONTINUE(method_name,97 AbstractMethodCheckerCases::eUnknownArgumentCount)98 }99 100 PythonCallable::ArgInfo arg_info = *arg_info_or_err;101 if (requirement.min_arg_count <= arg_info.max_positional_args) {102 SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eValid)103 } else {104 checker[method_name] = {105 AbstractMethodCheckerCases::eInvalidArgumentCount,106 AbstractMethodCheckerPayload::InvalidArgumentCountPayload(107 requirement.min_arg_count, arg_info.max_positional_args)};108 }109 }110 111#undef SET_CASE_AND_CONTINUE112 113 return checker;114 }115 116 template <typename... Args>117 llvm::Expected<StructuredData::GenericSP>118 CreatePluginObject(llvm::StringRef class_name,119 StructuredData::Generic *script_obj, Args... args) {120 using namespace python;121 using Locker = ScriptInterpreterPythonImpl::Locker;122 123 Log *log = GetLog(LLDBLog::Script);124 auto create_error = [](llvm::StringLiteral format, auto &&...ts) {125 return llvm::createStringError(126 llvm::formatv(format.data(), std::forward<decltype(ts)>(ts)...)127 .str());128 };129 130 bool has_class_name = !class_name.empty();131 bool has_interpreter_dict =132 !(llvm::StringRef(m_interpreter.GetDictionaryName()).empty());133 if (!has_class_name && !has_interpreter_dict && !script_obj) {134 if (!has_class_name)135 return create_error("Missing script class name.");136 else if (!has_interpreter_dict)137 return create_error("Invalid script interpreter dictionary.");138 else139 return create_error("Missing scripting object.");140 }141 142 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,143 Locker::FreeLock);144 145 PythonObject result = {};146 147 if (script_obj) {148 result = PythonObject(PyRefType::Borrowed,149 static_cast<PyObject *>(script_obj->GetValue()));150 } else {151 auto dict =152 PythonModule::MainModule().ResolveName<python::PythonDictionary>(153 m_interpreter.GetDictionaryName());154 if (!dict.IsAllocated())155 return create_error("Could not find interpreter dictionary: {0}",156 m_interpreter.GetDictionaryName());157 158 auto init =159 PythonObject::ResolveNameWithDictionary<python::PythonCallable>(160 class_name, dict);161 if (!init.IsAllocated())162 return create_error("Could not find script class: {0}",163 class_name.data());164 165 std::tuple<Args...> original_args = std::forward_as_tuple(args...);166 auto transformed_args = TransformArgs(original_args);167 168 std::string error_string;169 llvm::Expected<PythonCallable::ArgInfo> arg_info = init.GetArgInfo();170 if (!arg_info) {171 llvm::handleAllErrors(172 arg_info.takeError(),173 [&](PythonException &E) { error_string.append(E.ReadBacktrace()); },174 [&](const llvm::ErrorInfoBase &E) {175 error_string.append(E.message());176 });177 return llvm::createStringError(llvm::inconvertibleErrorCode(),178 error_string);179 }180 181 llvm::Expected<PythonObject> expected_return_object =182 create_error("Resulting object is not initialized.");183 184 // This relax the requirement on the number of argument for185 // initializing scripting extension if the size of the interface186 // parameter pack contains 1 less element than the extension maximum187 // number of positional arguments for this initializer.188 //189 // This addresses the cases where the embedded interpreter session190 // dictionary is passed to the extension initializer which is not used191 // most of the time.192 // Note, though none of our API's suggest defining the interfaces with193 // varargs, we have some extant clients that were doing that. To keep194 // from breaking them, we just say putting a varargs in these signatures195 // turns off argument checking.196 size_t num_args = sizeof...(Args);197 if (arg_info->max_positional_args != PythonCallable::ArgInfo::UNBOUNDED &&198 num_args != arg_info->max_positional_args) {199 if (num_args != arg_info->max_positional_args - 1)200 return create_error("Passed arguments ({0}) doesn't match the number "201 "of expected arguments ({1}).",202 num_args, arg_info->max_positional_args);203 204 std::apply(205 [&init, &expected_return_object](auto &&...args) {206 llvm::consumeError(expected_return_object.takeError());207 expected_return_object = init(args...);208 },209 std::tuple_cat(transformed_args, std::make_tuple(dict)));210 } else {211 std::apply(212 [&init, &expected_return_object](auto &&...args) {213 llvm::consumeError(expected_return_object.takeError());214 expected_return_object = init(args...);215 },216 transformed_args);217 }218 219 if (!expected_return_object)220 return expected_return_object.takeError();221 result = expected_return_object.get();222 }223 224 if (!result.IsValid())225 return create_error("Resulting object is not a valid Python Object.");226 if (!result.HasAttribute("__class__"))227 return create_error("Resulting object doesn't have '__class__' member.");228 229 PythonObject obj_class = result.GetAttributeValue("__class__");230 if (!obj_class.IsValid())231 return create_error("Resulting class object is not a valid.");232 if (!obj_class.HasAttribute("__name__"))233 return create_error(234 "Resulting object class doesn't have '__name__' member.");235 PythonString obj_class_name =236 obj_class.GetAttributeValue("__name__").AsType<PythonString>();237 238 PythonObject object_class_mapping_proxy =239 obj_class.GetAttributeValue("__dict__");240 if (!obj_class.HasAttribute("__dict__"))241 return create_error(242 "Resulting object class doesn't have '__dict__' member.");243 244 PythonCallable dict_converter = PythonModule::BuiltinsModule()245 .ResolveName("dict")246 .AsType<PythonCallable>();247 if (!dict_converter.IsAllocated())248 return create_error(249 "Python 'builtins' module doesn't have 'dict' class.");250 251 PythonDictionary object_class_dict =252 dict_converter(object_class_mapping_proxy).AsType<PythonDictionary>();253 if (!object_class_dict.IsAllocated())254 return create_error("Coudn't create dictionary from resulting object "255 "class mapping proxy object.");256 257 auto checker_or_err = CheckAbstractMethodImplementation(object_class_dict);258 if (!checker_or_err)259 return checker_or_err.takeError();260 261 llvm::Error abstract_method_errors = llvm::Error::success();262 for (const auto &method_checker : *checker_or_err)263 switch (method_checker.second.checker_case) {264 case AbstractMethodCheckerCases::eNotImplemented:265 abstract_method_errors = llvm::joinErrors(266 std::move(abstract_method_errors),267 std::move(create_error("Abstract method {0}.{1} not implemented.",268 obj_class_name.GetString(),269 method_checker.first)));270 break;271 case AbstractMethodCheckerCases::eNotAllocated:272 abstract_method_errors = llvm::joinErrors(273 std::move(abstract_method_errors),274 std::move(create_error("Abstract method {0}.{1} not allocated.",275 obj_class_name.GetString(),276 method_checker.first)));277 break;278 case AbstractMethodCheckerCases::eNotCallable:279 abstract_method_errors = llvm::joinErrors(280 std::move(abstract_method_errors),281 std::move(create_error("Abstract method {0}.{1} not callable.",282 obj_class_name.GetString(),283 method_checker.first)));284 break;285 case AbstractMethodCheckerCases::eUnknownArgumentCount:286 abstract_method_errors = llvm::joinErrors(287 std::move(abstract_method_errors),288 std::move(create_error(289 "Abstract method {0}.{1} has unknown argument count.",290 obj_class_name.GetString(), method_checker.first)));291 break;292 case AbstractMethodCheckerCases::eInvalidArgumentCount: {293 auto &payload_variant = method_checker.second.payload;294 if (!std::holds_alternative<295 AbstractMethodCheckerPayload::InvalidArgumentCountPayload>(296 payload_variant)) {297 abstract_method_errors = llvm::joinErrors(298 std::move(abstract_method_errors),299 std::move(create_error(300 "Abstract method {0}.{1} has unexpected argument count.",301 obj_class_name.GetString(), method_checker.first)));302 } else {303 auto payload = std::get<304 AbstractMethodCheckerPayload::InvalidArgumentCountPayload>(305 payload_variant);306 abstract_method_errors = llvm::joinErrors(307 std::move(abstract_method_errors),308 std::move(309 create_error("Abstract method {0}.{1} has unexpected "310 "argument count (expected {2} but has {3}).",311 obj_class_name.GetString(), method_checker.first,312 payload.required_argument_count,313 payload.actual_argument_count)));314 }315 } break;316 case AbstractMethodCheckerCases::eValid:317 LLDB_LOG(log, "Abstract method {0}.{1} implemented & valid.",318 obj_class_name.GetString(), method_checker.first);319 break;320 }321 322 if (abstract_method_errors) {323 Status error = Status::FromError(std::move(abstract_method_errors));324 LLDB_LOG(log, "Abstract method error in {0}:\n{1}", class_name,325 error.AsCString());326 return error.ToError();327 }328 329 m_object_instance_sp = StructuredData::GenericSP(330 new StructuredPythonObject(std::move(result)));331 return m_object_instance_sp;332 }333 334protected:335 template <typename T = StructuredData::ObjectSP>336 T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {337 return p.CreateStructuredObject();338 }339 340 template <typename T = StructuredData::ObjectSP, typename... Args>341 T Dispatch(llvm::StringRef method_name, Status &error, Args &&...args) {342 using namespace python;343 using Locker = ScriptInterpreterPythonImpl::Locker;344 345 std::string caller_signature =346 llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") +347 llvm::Twine(method_name) + llvm::Twine(")"))348 .str();349 if (!m_object_instance_sp)350 return ErrorWithMessage<T>(caller_signature, "Python object ill-formed",351 error);352 353 Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,354 Locker::FreeLock);355 356 PythonObject implementor(PyRefType::Borrowed,357 (PyObject *)m_object_instance_sp->GetValue());358 359 if (!implementor.IsAllocated())360 return llvm::is_contained(GetAbstractMethods(), method_name)361 ? ErrorWithMessage<T>(caller_signature,362 "Python implementor not allocated.",363 error)364 : T{};365 366 std::tuple<Args...> original_args = std::forward_as_tuple(args...);367 auto transformed_args = TransformArgs(original_args);368 369 llvm::Expected<PythonObject> expected_return_object =370 llvm::make_error<llvm::StringError>("Not initialized.",371 llvm::inconvertibleErrorCode());372 std::apply(373 [&implementor, &method_name, &expected_return_object](auto &&...args) {374 llvm::consumeError(expected_return_object.takeError());375 expected_return_object =376 implementor.CallMethod(method_name.data(), args...);377 },378 transformed_args);379 380 if (llvm::Error e = expected_return_object.takeError()) {381 error = Status::FromError(std::move(e));382 return ErrorWithMessage<T>(caller_signature,383 "Python method could not be called.", error);384 }385 386 PythonObject py_return = std::move(expected_return_object.get());387 388 // Now that we called the python method with the transformed arguments,389 // we need to interate again over both the original and transformed390 // parameter pack, and transform back the parameter that were passed in391 // the original parameter pack as references or pointers.392 if (sizeof...(Args) > 0)393 if (!ReassignPtrsOrRefsArgs(original_args, transformed_args))394 return ErrorWithMessage<T>(395 caller_signature,396 "Couldn't re-assign reference and pointer arguments.", error);397 398 if (!py_return.IsAllocated())399 return {};400 return ExtractValueFromPythonObject<T>(py_return, error);401 }402 403 template <typename... Args>404 Status GetStatusFromMethod(llvm::StringRef method_name, Args &&...args) {405 Status error;406 Dispatch<Status>(method_name, error, std::forward<Args>(args)...);407 408 return error;409 }410 411 template <typename T> T Transform(T object) {412 // No Transformation for generic usage413 return {object};414 }415 416 python::PythonObject Transform(bool arg) {417 // Boolean arguments need to be turned into python objects.418 return python::PythonBoolean(arg);419 }420 421 python::PythonObject Transform(const Status &arg) {422 return python::SWIGBridge::ToSWIGWrapper(arg.Clone());423 }424 425 python::PythonObject Transform(Status &&arg) {426 return python::SWIGBridge::ToSWIGWrapper(std::move(arg));427 }428 429 python::PythonObject Transform(const StructuredDataImpl &arg) {430 return python::SWIGBridge::ToSWIGWrapper(arg);431 }432 433 python::PythonObject Transform(lldb::ExecutionContextRefSP arg) {434 return python::SWIGBridge::ToSWIGWrapper(arg);435 }436 437 python::PythonObject Transform(lldb::TargetSP arg) {438 return python::SWIGBridge::ToSWIGWrapper(arg);439 }440 441 python::PythonObject Transform(lldb::BreakpointSP arg) {442 return python::SWIGBridge::ToSWIGWrapper(arg);443 }444 445 python::PythonObject Transform(lldb::BreakpointLocationSP arg) {446 return python::SWIGBridge::ToSWIGWrapper(arg);447 }448 449 python::PythonObject Transform(lldb::ProcessSP arg) {450 return python::SWIGBridge::ToSWIGWrapper(arg);451 }452 453 python::PythonObject Transform(lldb::ThreadSP arg) {454 return python::SWIGBridge::ToSWIGWrapper(arg);455 }456 457 python::PythonObject Transform(lldb::StackFrameListSP arg) {458 return python::SWIGBridge::ToSWIGWrapper(arg);459 }460 461 python::PythonObject Transform(lldb::ThreadPlanSP arg) {462 return python::SWIGBridge::ToSWIGWrapper(arg);463 }464 465 python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {466 return python::SWIGBridge::ToSWIGWrapper(arg);467 }468 469 python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {470 return python::SWIGBridge::ToSWIGWrapper(arg);471 }472 473 python::PythonObject Transform(Event *arg) {474 return python::SWIGBridge::ToSWIGWrapper(arg);475 }476 477 python::PythonObject Transform(const SymbolContext &arg) {478 return python::SWIGBridge::ToSWIGWrapper(arg);479 }480 481 python::PythonObject Transform(lldb::StreamSP arg) {482 return python::SWIGBridge::ToSWIGWrapper(arg.get());483 }484 485 python::PythonObject Transform(lldb::StackFrameSP arg) {486 return python::SWIGBridge::ToSWIGWrapper(arg);487 }488 489 python::PythonObject Transform(lldb::DataExtractorSP arg) {490 return python::SWIGBridge::ToSWIGWrapper(arg);491 }492 493 python::PythonObject Transform(lldb::DescriptionLevel arg) {494 return python::SWIGBridge::ToSWIGWrapper(arg);495 }496 497 template <typename T, typename U>498 void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {499 // If U is not a PythonObject, don't touch it!500 }501 502 template <typename T>503 void ReverseTransform(T &original_arg, python::PythonObject transformed_arg,504 Status &error) {505 original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);506 }507 508 void ReverseTransform(bool &original_arg,509 python::PythonObject transformed_arg, Status &error) {510 python::PythonBoolean boolean_arg = python::PythonBoolean(511 python::PyRefType::Borrowed, transformed_arg.get());512 if (boolean_arg.IsValid())513 original_arg = boolean_arg.GetValue();514 else515 error = Status::FromErrorStringWithFormatv(516 "{}: Invalid boolean argument.", LLVM_PRETTY_FUNCTION);517 }518 519 template <std::size_t... I, typename... Args>520 auto TransformTuple(const std::tuple<Args...> &args,521 std::index_sequence<I...>) {522 return std::make_tuple(Transform(std::get<I>(args))...);523 }524 525 // This will iterate over the Dispatch parameter pack and replace in-place526 // every `lldb_private` argument that has a SB counterpart.527 template <typename... Args>528 auto TransformArgs(const std::tuple<Args...> &args) {529 return TransformTuple(args, std::make_index_sequence<sizeof...(Args)>());530 }531 532 template <typename T, typename U>533 void TransformBack(T &original_arg, U transformed_arg, Status &error) {534 ReverseTransform(original_arg, transformed_arg, error);535 }536 537 template <std::size_t... I, typename... Ts, typename... Us>538 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,539 std::tuple<Us...> &transformed_args,540 std::index_sequence<I...>) {541 Status error;542 (TransformBack(std::get<I>(original_args), std::get<I>(transformed_args),543 error),544 ...);545 return error.Success();546 }547 548 template <typename... Ts, typename... Us>549 bool ReassignPtrsOrRefsArgs(std::tuple<Ts...> &original_args,550 std::tuple<Us...> &transformed_args) {551 if (sizeof...(Ts) != sizeof...(Us))552 return false;553 554 return ReassignPtrsOrRefsArgs(original_args, transformed_args,555 std::make_index_sequence<sizeof...(Ts)>());556 }557 558 template <typename T, typename... Args>559 void FormatArgs(std::string &fmt, T arg, Args... args) const {560 FormatArgs(fmt, arg);561 FormatArgs(fmt, args...);562 }563 564 template <typename T> void FormatArgs(std::string &fmt, T arg) const {565 fmt += python::PythonFormat<T>::format;566 }567 568 void FormatArgs(std::string &fmt) const {}569 570 // The lifetime is managed by the ScriptInterpreter571 ScriptInterpreterPythonImpl &m_interpreter;572};573 574template <>575StructuredData::ArraySP576ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(577 python::PythonObject &p, Status &error);578 579template <>580StructuredData::DictionarySP581ScriptedPythonInterface::ExtractValueFromPythonObject<582 StructuredData::DictionarySP>(python::PythonObject &p, Status &error);583 584template <>585Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(586 python::PythonObject &p, Status &error);587 588template <>589Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>(590 python::PythonObject &p, Status &error);591 592template <>593SymbolContext594ScriptedPythonInterface::ExtractValueFromPythonObject<SymbolContext>(595 python::PythonObject &p, Status &error);596 597template <>598lldb::StreamSP599ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(600 python::PythonObject &p, Status &error);601 602template <>603lldb::StackFrameSP604ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>(605 python::PythonObject &p, Status &error);606 607template <>608lldb::BreakpointSP609ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(610 python::PythonObject &p, Status &error);611 612template <>613lldb::BreakpointLocationSP614ScriptedPythonInterface::ExtractValueFromPythonObject<615 lldb::BreakpointLocationSP>(python::PythonObject &p, Status &error);616 617template <>618lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<619 lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);620 621template <>622lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<623 lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);624 625template <>626lldb::DataExtractorSP627ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(628 python::PythonObject &p, Status &error);629 630template <>631std::optional<MemoryRegionInfo>632ScriptedPythonInterface::ExtractValueFromPythonObject<633 std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);634 635template <>636lldb::ExecutionContextRefSP637ScriptedPythonInterface::ExtractValueFromPythonObject<638 lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error);639 640template <>641lldb::DescriptionLevel642ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DescriptionLevel>(643 python::PythonObject &p, Status &error);644 645template <>646lldb::StackFrameListSP647ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameListSP>(648 python::PythonObject &p, Status &error);649 650} // namespace lldb_private651 652#endif // LLDB_ENABLE_PYTHON653#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H654