1//===-- PythonDataObjects.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// 10// !! FIXME FIXME FIXME !! 11// 12// Python APIs nearly all can return an exception. They do this 13// by returning NULL, or -1, or some such value and setting 14// the exception state with PyErr_Set*(). Exceptions must be 15// handled before further python API functions are called. Failure 16// to do so will result in asserts on debug builds of python. 17// It will also sometimes, but not usually result in crashes of 18// release builds. 19// 20// Nearly all the code in this header does not handle python exceptions 21// correctly. It should all be converted to return Expected<> or 22// Error types to capture the exception. 23// 24// Everything in this file except functions that return Error or 25// Expected<> is considered deprecated and should not be 26// used in new code. If you need to use it, fix it first. 27// 28// 29// TODOs for this file 30// 31// * Make all methods safe for exceptions. 32// 33// * Eliminate method signatures that must translate exceptions into 34// empty objects or NULLs. Almost everything here should return 35// Expected<>. It should be acceptable for certain operations that 36// can never fail to assert instead, such as the creation of 37// PythonString from a string literal. 38// 39// * Elimintate Reset(), and make all non-default constructors private. 40// Python objects should be created with Retain<> or Take<>, and they 41// should be assigned with operator= 42// 43// * Eliminate default constructors, make python objects always 44// nonnull, and use optionals where necessary. 45// 46 47 48#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H 49#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H 50 51#include "lldb/Host/Config.h" 52 53#if LLDB_ENABLE_PYTHON 54 55// LLDB Python header must be included first 56#include "lldb-python.h" 57 58#include "lldb/Host/File.h" 59#include "lldb/Utility/StructuredData.h" 60 61#include "llvm/ADT/ArrayRef.h" 62 63namespace lldb_private { 64namespace python { 65 66class PythonObject; 67class PythonBytes; 68class PythonString; 69class PythonList; 70class PythonDictionary; 71class PythonInteger; 72class PythonException; 73 74class StructuredPythonObject : public StructuredData::Generic { 75public: 76 StructuredPythonObject() : StructuredData::Generic() {} 77 78 StructuredPythonObject(void *obj) : StructuredData::Generic(obj) { 79 Py_XINCREF(GetValue()); 80 } 81 82 ~StructuredPythonObject() override { 83 if (Py_IsInitialized()) 84 Py_XDECREF(GetValue()); 85 SetValue(nullptr); 86 } 87 88 bool IsValid() const override { return GetValue() && GetValue() != Py_None; } 89 90 void Serialize(llvm::json::OStream &s) const override; 91 92private: 93 DISALLOW_COPY_AND_ASSIGN(StructuredPythonObject); 94}; 95 96enum class PyObjectType { 97 Unknown, 98 None, 99 Boolean, 100 Integer, 101 Dictionary, 102 List, 103 String, 104 Bytes, 105 ByteArray, 106 Module, 107 Callable, 108 Tuple, 109 File 110}; 111 112enum class PyRefType { 113 Borrowed, // We are not given ownership of the incoming PyObject. 114 // We cannot safely hold it without calling Py_INCREF. 115 Owned // We have ownership of the incoming PyObject. We should 116 // not call Py_INCREF. 117}; 118 119 120// Take a reference that you already own, and turn it into 121// a PythonObject. 122// 123// Most python API methods will return a +1 reference 124// if they succeed or NULL if and only if 125// they set an exception. Use this to collect such return 126// values, after checking for NULL. 127// 128// If T is not just PythonObject, then obj must be already be 129// checked to be of the correct type. 130template <typename T> T Take(PyObject *obj) { 131 assert(obj); 132 assert(!PyErr_Occurred()); 133 T thing(PyRefType::Owned, obj); 134 assert(thing.IsValid()); 135 return thing; 136} 137 138// Retain a reference you have borrowed, and turn it into 139// a PythonObject. 140// 141// A minority of python APIs return a borrowed reference 142// instead of a +1. They will also return NULL if and only 143// if they set an exception. Use this to collect such return 144// values, after checking for NULL. 145// 146// If T is not just PythonObject, then obj must be already be 147// checked to be of the correct type. 148template <typename T> T Retain(PyObject *obj) { 149 assert(obj); 150 assert(!PyErr_Occurred()); 151 T thing(PyRefType::Borrowed, obj); 152 assert(thing.IsValid()); 153 return thing; 154} 155 156// This class can be used like a utility function to convert from 157// a llvm-friendly Twine into a null-terminated const char *, 158// which is the form python C APIs want their strings in. 159// 160// Example: 161// const llvm::Twine &some_twine; 162// PyFoo_Bar(x, y, z, NullTerminated(some_twine)); 163// 164// Why a class instead of a function? If the twine isn't already null 165// terminated, it will need a temporary buffer to copy the string 166// into. We need that buffer to stick around for the lifetime of the 167// statement. 168class NullTerminated { 169 const char *str; 170 llvm::SmallString<32> storage; 171 172public: 173 NullTerminated(const llvm::Twine &twine) { 174 llvm::StringRef ref = twine.toNullTerminatedStringRef(storage); 175 str = ref.begin(); 176 } 177 operator const char *() { return str; } 178}; 179 180inline llvm::Error nullDeref() { 181 return llvm::createStringError(llvm::inconvertibleErrorCode(), 182 "A NULL PyObject* was dereferenced"); 183} 184 185inline llvm::Error exception(const char *s = nullptr) { 186 return llvm::make_error<PythonException>(s); 187} 188 189inline llvm::Error keyError() { 190 return llvm::createStringError(llvm::inconvertibleErrorCode(), 191 "key not in dict"); 192} 193 194#if PY_MAJOR_VERSION < 3 195// The python 2 API declares some arguments as char* that should 196// be const char *, but it doesn't actually modify them. 197inline char *py2_const_cast(const char *s) { return const_cast<char *>(s); } 198#else 199inline const char *py2_const_cast(const char *s) { return s; } 200#endif 201 202enum class PyInitialValue { Invalid, Empty }; 203 204template <typename T, typename Enable = void> struct PythonFormat; 205 206template <> struct PythonFormat<unsigned long long> { 207 static constexpr char format = 'K'; 208 static auto get(unsigned long long value) { return value; } 209}; 210 211template <> struct PythonFormat<long long> { 212 static constexpr char format = 'L'; 213 static auto get(long long value) { return value; } 214}; 215 216template <> struct PythonFormat<PyObject *> { 217 static constexpr char format = 'O'; 218 static auto get(PyObject *value) { return value; } 219}; 220 221template <typename T> 222struct PythonFormat< 223 T, typename std::enable_if<std::is_base_of<PythonObject, T>::value>::type> { 224 static constexpr char format = 'O'; 225 static auto get(const T &value) { return value.get(); } 226}; 227 228class PythonObject { 229public: 230 PythonObject() : m_py_obj(nullptr) {} 231 232 PythonObject(PyRefType type, PyObject *py_obj) { 233 m_py_obj = py_obj; 234 // If this is a borrowed reference, we need to convert it to 235 // an owned reference by incrementing it. If it is an owned 236 // reference (for example the caller allocated it with PyDict_New() 237 // then we must *not* increment it. 238 if (m_py_obj && Py_IsInitialized() && type == PyRefType::Borrowed) 239 Py_XINCREF(m_py_obj); 240 } 241 242 PythonObject(const PythonObject &rhs) 243 : PythonObject(PyRefType::Borrowed, rhs.m_py_obj) {} 244 245 PythonObject(PythonObject &&rhs) { 246 m_py_obj = rhs.m_py_obj; 247 rhs.m_py_obj = nullptr; 248 } 249 250 ~PythonObject() { Reset(); } 251 252 void Reset() { 253 if (m_py_obj && Py_IsInitialized()) 254 Py_DECREF(m_py_obj); 255 m_py_obj = nullptr; 256 } 257 258 void Dump() const { 259 if (m_py_obj) 260 _PyObject_Dump(m_py_obj); 261 else 262 puts("NULL"); 263 } 264 265 void Dump(Stream &strm) const; 266 267 PyObject *get() const { return m_py_obj; } 268 269 PyObject *release() { 270 PyObject *result = m_py_obj; 271 m_py_obj = nullptr; 272 return result; 273 } 274 275 PythonObject &operator=(PythonObject other) { 276 Reset(); 277 m_py_obj = std::exchange(other.m_py_obj, nullptr); 278 return *this; 279 } 280 281 PyObjectType GetObjectType() const; 282 283 PythonString Repr() const; 284 285 PythonString Str() const; 286 287 static PythonObject ResolveNameWithDictionary(llvm::StringRef name, 288 const PythonDictionary &dict); 289 290 template <typename T> 291 static T ResolveNameWithDictionary(llvm::StringRef name, 292 const PythonDictionary &dict) { 293 return ResolveNameWithDictionary(name, dict).AsType<T>(); 294 } 295 296 PythonObject ResolveName(llvm::StringRef name) const; 297 298 template <typename T> T ResolveName(llvm::StringRef name) const { 299 return ResolveName(name).AsType<T>(); 300 } 301 302 bool HasAttribute(llvm::StringRef attribute) const; 303 304 PythonObject GetAttributeValue(llvm::StringRef attribute) const; 305 306 bool IsNone() const { return m_py_obj == Py_None; } 307 308 bool IsValid() const { return m_py_obj != nullptr; } 309 310 bool IsAllocated() const { return IsValid() && !IsNone(); } 311 312 explicit operator bool() const { return IsValid() && !IsNone(); } 313 314 template <typename T> T AsType() const { 315 if (!T::Check(m_py_obj)) 316 return T(); 317 return T(PyRefType::Borrowed, m_py_obj); 318 } 319 320 StructuredData::ObjectSP CreateStructuredObject() const; 321 322public: 323 template <typename... T> 324 llvm::Expected<PythonObject> CallMethod(const char *name, 325 const T &... t) const { 326 const char format[] = {'(', PythonFormat<T>::format..., ')', 0}; 327 PyObject *obj = 328 PyObject_CallMethod(m_py_obj, py2_const_cast(name), 329 py2_const_cast(format), PythonFormat<T>::get(t)...); 330 if (!obj) 331 return exception(); 332 return python::Take<PythonObject>(obj); 333 } 334 335 template <typename... T> 336 llvm::Expected<PythonObject> Call(const T &... t) const { 337 const char format[] = {'(', PythonFormat<T>::format..., ')', 0}; 338 PyObject *obj = PyObject_CallFunction(m_py_obj, py2_const_cast(format), 339 PythonFormat<T>::get(t)...); 340 if (!obj) 341 return exception(); 342 return python::Take<PythonObject>(obj); 343 } 344 345 llvm::Expected<PythonObject> GetAttribute(const llvm::Twine &name) const { 346 if (!m_py_obj) 347 return nullDeref(); 348 PyObject *obj = PyObject_GetAttrString(m_py_obj, NullTerminated(name)); 349 if (!obj) 350 return exception(); 351 return python::Take<PythonObject>(obj); 352 } 353 354 llvm::Expected<bool> IsTrue() { 355 if (!m_py_obj) 356 return nullDeref(); 357 int r = PyObject_IsTrue(m_py_obj); 358 if (r < 0) 359 return exception(); 360 return !!r; 361 } 362 363 llvm::Expected<long long> AsLongLong() { 364 if (!m_py_obj) 365 return nullDeref(); 366 assert(!PyErr_Occurred()); 367 long long r = PyLong_AsLongLong(m_py_obj); 368 if (PyErr_Occurred()) 369 return exception(); 370 return r; 371 } 372 373 llvm::Expected<bool> IsInstance(const PythonObject &cls) { 374 if (!m_py_obj || !cls.IsValid()) 375 return nullDeref(); 376 int r = PyObject_IsInstance(m_py_obj, cls.get()); 377 if (r < 0) 378 return exception(); 379 return !!r; 380 } 381 382protected: 383 PyObject *m_py_obj; 384}; 385 386 387// This is why C++ needs monads. 388template <typename T> llvm::Expected<T> As(llvm::Expected<PythonObject> &&obj) { 389 if (!obj) 390 return obj.takeError(); 391 if (!T::Check(obj.get().get())) 392 return llvm::createStringError(llvm::inconvertibleErrorCode(), 393 "type error"); 394 return T(PyRefType::Borrowed, std::move(obj.get().get())); 395} 396 397template <> llvm::Expected<bool> As<bool>(llvm::Expected<PythonObject> &&obj); 398 399template <> 400llvm::Expected<long long> As<long long>(llvm::Expected<PythonObject> &&obj); 401 402template <> 403llvm::Expected<std::string> As<std::string>(llvm::Expected<PythonObject> &&obj); 404 405 406template <class T> class TypedPythonObject : public PythonObject { 407public: 408 // override to perform implicit type conversions on Reset 409 // This can be eliminated once we drop python 2 support. 410 static void Convert(PyRefType &type, PyObject *&py_obj) {} 411 412 TypedPythonObject(PyRefType type, PyObject *py_obj) { 413 if (!py_obj) 414 return; 415 T::Convert(type, py_obj); 416 if (T::Check(py_obj)) 417 PythonObject::operator=(PythonObject(type, py_obj)); 418 else if (type == PyRefType::Owned) 419 Py_DECREF(py_obj); 420 } 421 422 TypedPythonObject() {} 423}; 424 425class PythonBytes : public TypedPythonObject<PythonBytes> { 426public: 427 using TypedPythonObject::TypedPythonObject; 428 explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes); 429 PythonBytes(const uint8_t *bytes, size_t length); 430 431 static bool Check(PyObject *py_obj); 432 433 llvm::ArrayRef<uint8_t> GetBytes() const; 434 435 size_t GetSize() const; 436 437 void SetBytes(llvm::ArrayRef<uint8_t> stringbytes); 438 439 StructuredData::StringSP CreateStructuredString() const; 440}; 441 442class PythonByteArray : public TypedPythonObject<PythonByteArray> { 443public: 444 using TypedPythonObject::TypedPythonObject; 445 explicit PythonByteArray(llvm::ArrayRef<uint8_t> bytes); 446 PythonByteArray(const uint8_t *bytes, size_t length); 447 PythonByteArray(const PythonBytes &object); 448 449 static bool Check(PyObject *py_obj); 450 451 llvm::ArrayRef<uint8_t> GetBytes() const; 452 453 size_t GetSize() const; 454 455 void SetBytes(llvm::ArrayRef<uint8_t> stringbytes); 456 457 StructuredData::StringSP CreateStructuredString() const; 458}; 459 460class PythonString : public TypedPythonObject<PythonString> { 461public: 462 using TypedPythonObject::TypedPythonObject; 463 static llvm::Expected<PythonString> FromUTF8(llvm::StringRef string); 464 465 PythonString() : TypedPythonObject() {} // MSVC requires this for some reason 466 467 explicit PythonString(llvm::StringRef string); // safe, null on error 468 469 static bool Check(PyObject *py_obj); 470 static void Convert(PyRefType &type, PyObject *&py_obj); 471 472 llvm::StringRef GetString() const; // safe, empty string on error 473 474 llvm::Expected<llvm::StringRef> AsUTF8() const; 475 476 size_t GetSize() const; 477 478 void SetString(llvm::StringRef string); // safe, null on error 479 480 StructuredData::StringSP CreateStructuredString() const; 481}; 482 483class PythonInteger : public TypedPythonObject<PythonInteger> { 484public: 485 using TypedPythonObject::TypedPythonObject; 486 487 PythonInteger() : TypedPythonObject() {} // MSVC requires this for some reason 488 489 explicit PythonInteger(int64_t value); 490 491 static bool Check(PyObject *py_obj); 492 static void Convert(PyRefType &type, PyObject *&py_obj); 493 494 int64_t GetInteger() const; 495 496 void SetInteger(int64_t value); 497 498 StructuredData::IntegerSP CreateStructuredInteger() const; 499}; 500 501class PythonBoolean : public TypedPythonObject<PythonBoolean> { 502public: 503 using TypedPythonObject::TypedPythonObject; 504 505 explicit PythonBoolean(bool value); 506 507 static bool Check(PyObject *py_obj); 508 509 bool GetValue() const; 510 511 void SetValue(bool value); 512 513 StructuredData::BooleanSP CreateStructuredBoolean() const; 514}; 515 516class PythonList : public TypedPythonObject<PythonList> { 517public: 518 using TypedPythonObject::TypedPythonObject; 519 520 PythonList() : TypedPythonObject() {} // MSVC requires this for some reason 521 522 explicit PythonList(PyInitialValue value); 523 explicit PythonList(int list_size); 524 525 static bool Check(PyObject *py_obj); 526 527 uint32_t GetSize() const; 528 529 PythonObject GetItemAtIndex(uint32_t index) const; 530 531 void SetItemAtIndex(uint32_t index, const PythonObject &object); 532 533 void AppendItem(const PythonObject &object); 534 535 StructuredData::ArraySP CreateStructuredArray() const; 536}; 537 538class PythonTuple : public TypedPythonObject<PythonTuple> { 539public: 540 using TypedPythonObject::TypedPythonObject; 541 542 explicit PythonTuple(PyInitialValue value); 543 explicit PythonTuple(int tuple_size); 544 PythonTuple(std::initializer_list<PythonObject> objects); 545 PythonTuple(std::initializer_list<PyObject *> objects); 546 547 static bool Check(PyObject *py_obj); 548 549 uint32_t GetSize() const; 550 551 PythonObject GetItemAtIndex(uint32_t index) const; 552 553 void SetItemAtIndex(uint32_t index, const PythonObject &object); 554 555 StructuredData::ArraySP CreateStructuredArray() const; 556}; 557 558class PythonDictionary : public TypedPythonObject<PythonDictionary> { 559public: 560 using TypedPythonObject::TypedPythonObject; 561 562 PythonDictionary() : TypedPythonObject() {} // MSVC requires this for some reason 563 564 explicit PythonDictionary(PyInitialValue value); 565 566 static bool Check(PyObject *py_obj); 567 568 uint32_t GetSize() const; 569 570 PythonList GetKeys() const; 571 572 PythonObject GetItemForKey(const PythonObject &key) const; // DEPRECATED 573 void SetItemForKey(const PythonObject &key, 574 const PythonObject &value); // DEPRECATED 575 576 llvm::Expected<PythonObject> GetItem(const PythonObject &key) const; 577 llvm::Expected<PythonObject> GetItem(const llvm::Twine &key) const; 578 llvm::Error SetItem(const PythonObject &key, const PythonObject &value) const; 579 llvm::Error SetItem(const llvm::Twine &key, const PythonObject &value) const; 580 581 StructuredData::DictionarySP CreateStructuredDictionary() const; 582}; 583 584class PythonModule : public TypedPythonObject<PythonModule> { 585public: 586 using TypedPythonObject::TypedPythonObject; 587 588 static bool Check(PyObject *py_obj); 589 590 static PythonModule BuiltinsModule(); 591 592 static PythonModule MainModule(); 593 594 static PythonModule AddModule(llvm::StringRef module); 595 596 // safe, returns invalid on error; 597 static PythonModule ImportModule(llvm::StringRef name) { 598 std::string s = name; 599 auto mod = Import(s.c_str()); 600 if (!mod) { 601 llvm::consumeError(mod.takeError()); 602 return PythonModule(); 603 } 604 return std::move(mod.get()); 605 } 606 607 static llvm::Expected<PythonModule> Import(const llvm::Twine &name); 608 609 llvm::Expected<PythonObject> Get(const llvm::Twine &name); 610 611 PythonDictionary GetDictionary() const; 612}; 613 614class PythonCallable : public TypedPythonObject<PythonCallable> { 615public: 616 using TypedPythonObject::TypedPythonObject; 617 618 struct ArgInfo { 619 /* the largest number of positional arguments this callable 620 * can accept, or UNBOUNDED, ie UINT_MAX if it's a varargs 621 * function and can accept an arbitrary number */ 622 unsigned max_positional_args; 623 static constexpr unsigned UNBOUNDED = UINT_MAX; // FIXME c++17 inline 624 }; 625 626 static bool Check(PyObject *py_obj); 627 628 llvm::Expected<ArgInfo> GetArgInfo() const; 629 630 PythonObject operator()(); 631 632 PythonObject operator()(std::initializer_list<PyObject *> args); 633 634 PythonObject operator()(std::initializer_list<PythonObject> args); 635 636 template <typename Arg, typename... Args> 637 PythonObject operator()(const Arg &arg, Args... args) { 638 return operator()({arg, args...}); 639 } 640}; 641 642class PythonFile : public TypedPythonObject<PythonFile> { 643public: 644 using TypedPythonObject::TypedPythonObject; 645 646 PythonFile() : TypedPythonObject() {} // MSVC requires this for some reason 647 648 static bool Check(PyObject *py_obj); 649 650 static llvm::Expected<PythonFile> FromFile(File &file, 651 const char *mode = nullptr); 652 653 llvm::Expected<lldb::FileSP> ConvertToFile(bool borrowed = false); 654 llvm::Expected<lldb::FileSP> 655 ConvertToFileForcingUseOfScriptingIOMethods(bool borrowed = false); 656}; 657 658class PythonException : public llvm::ErrorInfo<PythonException> { 659private: 660 PyObject *m_exception_type, *m_exception, *m_traceback; 661 PyObject *m_repr_bytes; 662 663public: 664 static char ID; 665 const char *toCString() const; 666 PythonException(const char *caller = nullptr); 667 void Restore(); 668 ~PythonException(); 669 void log(llvm::raw_ostream &OS) const override; 670 std::error_code convertToErrorCode() const override; 671 bool Matches(PyObject *exc) const; 672 std::string ReadBacktrace() const; 673}; 674 675// This extracts the underlying T out of an Expected<T> and returns it. 676// If the Expected is an Error instead of a T, that error will be converted 677// into a python exception, and this will return a default-constructed T. 678// 679// This is appropriate for use right at the boundary of python calling into 680// C++, such as in a SWIG typemap. In such a context you should simply 681// check if the returned T is valid, and if it is, return a NULL back 682// to python. This will result in the Error being raised as an exception 683// from python code's point of view. 684// 685// For example: 686// ``` 687// Expected<Foo *> efoop = some_cpp_function(); 688// Foo *foop = unwrapOrSetPythonException(efoop); 689// if (!foop) 690// return NULL; 691// do_something(*foop); 692// 693// If the Error returned was itself created because a python exception was 694// raised when C++ code called into python, then the original exception 695// will be restored. Otherwise a simple string exception will be raised. 696template <typename T> T unwrapOrSetPythonException(llvm::Expected<T> expected) { 697 if (expected) 698 return expected.get(); 699 llvm::handleAllErrors( 700 expected.takeError(), [](PythonException &E) { E.Restore(); }, 701 [](const llvm::ErrorInfoBase &E) { 702 PyErr_SetString(PyExc_Exception, E.message().c_str()); 703 }); 704 return T(); 705} 706 707// This is only here to help incrementally migrate old, exception-unsafe 708// code. 709template <typename T> T unwrapIgnoringErrors(llvm::Expected<T> expected) { 710 if (expected) 711 return std::move(expected.get()); 712 llvm::consumeError(expected.takeError()); 713 return T(); 714} 715 716llvm::Expected<PythonObject> runStringOneLine(const llvm::Twine &string, 717 const PythonDictionary &globals, 718 const PythonDictionary &locals); 719 720llvm::Expected<PythonObject> runStringMultiLine(const llvm::Twine &string, 721 const PythonDictionary &globals, 722 const PythonDictionary &locals); 723 724// Sometimes the best way to interact with a python interpreter is 725// to run some python code. You construct a PythonScript with 726// script string. The script assigns some function to `_function_` 727// and you get a C++ callable object that calls the python function. 728// 729// Example: 730// 731// const char script[] = R"( 732// def main(x, y): 733// .... 734// )"; 735// 736// Expected<PythonObject> cpp_foo_wrapper(PythonObject x, PythonObject y) { 737// // no need to synchronize access to this global, we already have the GIL 738// static PythonScript foo(script) 739// return foo(x, y); 740// } 741class PythonScript { 742 const char *script; 743 PythonCallable function; 744 745 llvm::Error Init(); 746 747public: 748 PythonScript(const char *script) : script(script), function() {} 749 750 template <typename... Args> 751 llvm::Expected<PythonObject> operator()(Args &&... args) { 752 if (llvm::Error error = Init()) 753 return std::move(error); 754 return function.Call(std::forward<Args>(args)...); 755 } 756}; 757 758} // namespace python 759} // namespace lldb_private 760 761#endif 762 763#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H 764