diff --git a/lldb/bindings/headers.swig b/lldb/bindings/headers.swig --- a/lldb/bindings/headers.swig +++ b/lldb/bindings/headers.swig @@ -49,6 +49,7 @@ #include "lldb/API/SBQueue.h" #include "lldb/API/SBQueueItem.h" #include "lldb/API/SBReproducer.h" +#include "lldb/API/SBScriptObject.h" #include "lldb/API/SBSection.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" diff --git a/lldb/bindings/interface/SBScriptObjectExtensions.i b/lldb/bindings/interface/SBScriptObjectExtensions.i new file mode 100644 --- /dev/null +++ b/lldb/bindings/interface/SBScriptObjectExtensions.i @@ -0,0 +1,8 @@ +%extend lldb::SBScriptObject { +#ifdef SWIGPYTHON + %pythoncode %{ + ptr = property(GetPointer, None, doc='''A read only property that returns the underlying script object.''') + lang = property(GetLanguage, None, doc='''A read only property that returns the script language associated with with this script object.''') + %} +#endif +} diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig --- a/lldb/bindings/interfaces.swig +++ b/lldb/bindings/interfaces.swig @@ -124,6 +124,7 @@ %include "lldb/API/SBQueue.h" %include "lldb/API/SBQueueItem.h" %include "lldb/API/SBReproducer.h" +%include "lldb/API/SBScriptObject.h" %include "lldb/API/SBSection.h" %include "lldb/API/SBSourceManager.h" %include "lldb/API/SBStream.h" @@ -176,6 +177,7 @@ %include "./interface/SBModuleExtensions.i" %include "./interface/SBModuleSpecExtensions.i" %include "./interface/SBProcessExtensions.i" +%include "./interface/SBScriptObjectExtensions.i" %include "./interface/SBSectionExtensions.i" %include "./interface/SBStreamExtensions.i" %include "./interface/SBStringListExtensions.i" diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig --- a/lldb/bindings/python/python-typemaps.swig +++ b/lldb/bindings/python/python-typemaps.swig @@ -59,9 +59,78 @@ free((char *) $1); } -%typemap(out) lldb::ScriptedObject { +%typecheck(SWIG_TYPECHECK_POINTER) lldb::ScriptObjectPtr { + PythonObject obj(PyRefType::Borrowed, $input); + if (!obj.IsValid()) { + PyErr_Clear(); + $1 = 0; + } else { + $1 = 1; + } +} + +%typemap(in) lldb::ScriptObjectPtr { + if ($input == Py_None) { + $1 = nullptr; + } else { + PythonObject obj(PyRefType::Borrowed, $input); + if (!obj.IsValid()) { + PyErr_SetString(PyExc_TypeError, "Script object is not valid"); + SWIG_fail; + } + + auto lldb_module = PythonModule::Import("lldb"); + if (!lldb_module) { + std::string err_msg = llvm::toString(lldb_module.takeError()); + PyErr_SetString(PyExc_TypeError, err_msg.c_str()); + SWIG_fail; + } + + auto sb_structured_data_class = lldb_module.get().Get("SBStructuredData"); + if (!sb_structured_data_class) { + std::string err_msg = llvm::toString(sb_structured_data_class.takeError()); + PyErr_SetString(PyExc_TypeError, err_msg.c_str()); + SWIG_fail; + } + + if (obj.IsInstance(sb_structured_data_class.get())) { + $1 = obj.get(); + } else { + auto type = obj.GetType(); + if (!type) { + std::string err_msg = llvm::toString(type.takeError()); + PyErr_SetString(PyExc_TypeError, err_msg.c_str()); + SWIG_fail; + } + + auto type_name = As(type.get().GetAttribute("__name__")); + if (!type_name) { + std::string err_msg = llvm::toString(type_name.takeError()); + PyErr_SetString(PyExc_TypeError, err_msg.c_str()); + SWIG_fail; + } + + if (llvm::StringRef(type_name.get()).startswith("SB")) { + std::string error_msg = "Input type is invalid: " + type_name.get(); + PyErr_SetString(PyExc_TypeError, error_msg.c_str()); + SWIG_fail; + } else { + $1 = obj.get(); + } + } + } +} + +%typemap(out) lldb::ScriptObjectPtr { + $result = (PyObject*) $1; + if (!$result) + $result = Py_None; + Py_INCREF($result); +} + +%typemap(out) lldb::SBScriptObject { $result = nullptr; - if (const void* impl = $1) + if (const void* impl = $1.GetPointer()) $result = (PyObject*) impl; if (!$result) { $result = Py_None; diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h --- a/lldb/include/lldb/API/SBDebugger.h +++ b/lldb/include/lldb/API/SBDebugger.h @@ -484,6 +484,7 @@ friend class SBListener; friend class SBProcess; friend class SBSourceManager; + friend class SBStructuredData; friend class SBTarget; friend class SBTrace; diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h --- a/lldb/include/lldb/API/SBDefines.h +++ b/lldb/include/lldb/API/SBDefines.h @@ -88,6 +88,7 @@ class LLDB_API SBProcessInfo; class LLDB_API SBQueue; class LLDB_API SBQueueItem; +class LLDB_API SBScriptObject; class LLDB_API SBSection; class LLDB_API SBSourceManager; class LLDB_API SBStream; @@ -131,8 +132,6 @@ typedef SBError (*SBPlatformLocateModuleCallback)( void *baton, const SBModuleSpec &module_spec, SBFileSpec &module_file_spec, SBFileSpec &symbol_file_spec); - -typedef void *ScriptedObject; } #endif // LLDB_API_SBDEFINES_H diff --git a/lldb/include/lldb/API/SBProcess.h b/lldb/include/lldb/API/SBProcess.h --- a/lldb/include/lldb/API/SBProcess.h +++ b/lldb/include/lldb/API/SBProcess.h @@ -437,7 +437,7 @@ /// lldb::SBError DeallocateMemory(lldb::addr_t ptr); - lldb::ScriptedObject GetScriptedImplementation(); + lldb::SBScriptObject GetScriptedImplementation(); protected: friend class SBAddress; diff --git a/lldb/include/lldb/API/SBScriptObject.h b/lldb/include/lldb/API/SBScriptObject.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/API/SBScriptObject.h @@ -0,0 +1,55 @@ +//===-- SBScriptObject.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_API_SBSCRIPTOBJECT_H +#define LLDB_API_SBSCRIPTOBJECT_H + +#include "lldb/API/SBDefines.h" + +namespace lldb_private { +class ScriptObject; +} + +namespace lldb { + +class LLDB_API SBScriptObject { +public: + SBScriptObject(const ScriptObjectPtr ptr, lldb::ScriptLanguage lang); + + SBScriptObject(const lldb::SBScriptObject &rhs); + + ~SBScriptObject(); + + const lldb::SBScriptObject &operator=(const lldb::SBScriptObject &rhs); + + explicit operator bool() const; + + bool operator!=(const SBScriptObject &rhs) const; + + bool IsValid() const; + + lldb::ScriptObjectPtr GetPointer() const; + + lldb::ScriptLanguage GetLanguage() const; + +protected: + friend class SBStructuredData; + + lldb_private::ScriptObject *get(); + + lldb_private::ScriptObject &ref(); + + const lldb_private::ScriptObject &ref() const; + +private: + std::unique_ptr m_opaque_up; +}; + +} // namespace lldb + +#endif // LLDB_API_SBSCRIPTOBJECT_H diff --git a/lldb/include/lldb/API/SBStructuredData.h b/lldb/include/lldb/API/SBStructuredData.h --- a/lldb/include/lldb/API/SBStructuredData.h +++ b/lldb/include/lldb/API/SBStructuredData.h @@ -11,6 +11,7 @@ #include "lldb/API/SBDefines.h" #include "lldb/API/SBModule.h" +#include "lldb/API/SBScriptObject.h" namespace lldb_private { namespace python { @@ -29,6 +30,9 @@ SBStructuredData(const lldb::SBStructuredData &rhs); + SBStructuredData(const lldb::SBScriptObject obj, + const lldb::SBDebugger &debugger); + ~SBStructuredData(); lldb::SBStructuredData &operator=(const lldb::SBStructuredData &rhs); @@ -101,6 +105,9 @@ /// \a dst in all cases. size_t GetStringValue(char *dst, size_t dst_len) const; + /// Return the generic pointer if this data structure is a generic type. + lldb::SBScriptObject GetGenericValue() const; + protected: friend class SBAttachInfo; friend class SBLaunchInfo; diff --git a/lldb/include/lldb/Core/StructuredDataImpl.h b/lldb/include/lldb/Core/StructuredDataImpl.h --- a/lldb/include/lldb/Core/StructuredDataImpl.h +++ b/lldb/include/lldb/Core/StructuredDataImpl.h @@ -161,6 +161,17 @@ return (::snprintf(dst, dst_len, "%s", result.data())); } + void *GetGenericValue() const { + if (!m_data_sp) + return nullptr; + + StructuredData::Generic *generic_data = m_data_sp->GetAsGeneric(); + if (!generic_data) + return nullptr; + + return generic_data->GetValue(); + } + StructuredData::ObjectSP GetObjectSP() const { return m_data_sp; } private: diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -21,6 +21,7 @@ #include "lldb/Core/StreamFile.h" #include "lldb/Core/ThreadedCommunication.h" #include "lldb/Host/PseudoTerminal.h" +#include "lldb/Interpreter/ScriptObject.h" #include "lldb/Interpreter/ScriptedPlatformInterface.h" #include "lldb/Interpreter/ScriptedProcessInterface.h" #include "lldb/Utility/Broadcaster.h" @@ -591,6 +592,11 @@ return *m_scripted_platform_interface_up; } + virtual StructuredData::ObjectSP + CreateStructuredDataFromScriptObject(ScriptObject obj) { + return {}; + } + lldb::DataExtractorSP GetDataExtractorFromSBData(const lldb::SBData &data) const; diff --git a/lldb/include/lldb/Interpreter/ScriptObject.h b/lldb/include/lldb/Interpreter/ScriptObject.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Interpreter/ScriptObject.h @@ -0,0 +1,32 @@ +//===-- ScriptObject.h ------------------------------------ -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_INTERPRETER_SCRIPTOBJECT_H +#define LLDB_INTERPRETER_SCRIPTOBJECT_H + +#include "lldb/lldb-types.h" + +namespace lldb_private { +class ScriptObject { +public: + ScriptObject(lldb::ScriptObjectPtr ptr, lldb::ScriptLanguage lang) + : m_ptr(ptr), m_language(lang) {} + + operator bool() const { return m_ptr != nullptr; } + + const void *GetPointer() const { return m_ptr; } + + lldb::ScriptLanguage GetLanguage() const { return m_language; } + +private: + const void *m_ptr; + lldb::ScriptLanguage m_language; +}; +} // namespace lldb_private + +#endif // LLDB_INTERPRETER_SCRIPTOBJECT_H diff --git a/lldb/include/lldb/lldb-types.h b/lldb/include/lldb/lldb-types.h --- a/lldb/include/lldb/lldb-types.h +++ b/lldb/include/lldb/lldb-types.h @@ -74,6 +74,8 @@ typedef bool (*ExpressionCancelCallback)(ExpressionEvaluationPhase phase, void *baton); +typedef void *ScriptObjectPtr; + typedef uint64_t addr_t; typedef uint64_t user_id_t; typedef uint64_t pid_t; diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -64,6 +64,7 @@ SBQueue.cpp SBQueueItem.cpp SBReproducer.cpp + SBScriptObject.cpp SBSection.cpp SBSourceManager.cpp SBStream.cpp diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp --- a/lldb/source/API/SBProcess.cpp +++ b/lldb/source/API/SBProcess.cpp @@ -38,6 +38,7 @@ #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" +#include "lldb/API/SBScriptObject.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" @@ -1285,8 +1286,10 @@ return sb_error; } -ScriptedObject SBProcess::GetScriptedImplementation() { +lldb::SBScriptObject SBProcess::GetScriptedImplementation() { LLDB_INSTRUMENT_VA(this); ProcessSP process_sp(GetSP()); - return (process_sp) ? process_sp->GetImplementation() : nullptr; + return lldb::SBScriptObject((process_sp) ? process_sp->GetImplementation() + : nullptr, + eScriptLanguageDefault); } diff --git a/lldb/source/API/SBScriptObject.cpp b/lldb/source/API/SBScriptObject.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/API/SBScriptObject.cpp @@ -0,0 +1,84 @@ +//===-- SBScriptObject.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBScriptObject.h" + +#include "Utils.h" + +#include "lldb/Interpreter/ScriptObject.h" +#include "lldb/Utility/Instrumentation.h" + +using namespace lldb; +using namespace lldb_private; + +SBScriptObject::SBScriptObject(const ScriptObjectPtr ptr, + lldb::ScriptLanguage lang) + : m_opaque_up(std::make_unique(ptr, lang)) { + LLDB_INSTRUMENT_VA(this, ptr, lang); +} + +SBScriptObject::SBScriptObject(const SBScriptObject &rhs) + : m_opaque_up(new ScriptObject(nullptr, eScriptLanguageNone)) { + LLDB_INSTRUMENT_VA(this, rhs); + + m_opaque_up = clone(rhs.m_opaque_up); +} +SBScriptObject::~SBScriptObject() = default; + +const SBScriptObject &SBScriptObject::operator=(const SBScriptObject &rhs) { + LLDB_INSTRUMENT_VA(this, rhs); + + if (this != &rhs) + m_opaque_up = clone(rhs.m_opaque_up); + return *this; +} + +bool SBScriptObject::operator!=(const SBScriptObject &rhs) const { + LLDB_INSTRUMENT_VA(this, rhs); + + return !(m_opaque_up == rhs.m_opaque_up); +} + +bool SBScriptObject::IsValid() const { + LLDB_INSTRUMENT_VA(this); + + return this->operator bool(); +} + +SBScriptObject::operator bool() const { + LLDB_INSTRUMENT_VA(this); + + return m_opaque_up != nullptr && m_opaque_up->operator bool(); +} + +lldb::ScriptObjectPtr SBScriptObject::GetPointer() const { + LLDB_INSTRUMENT_VA(this); + + return m_opaque_up ? const_cast(m_opaque_up->GetPointer()) : nullptr; +} + +lldb::ScriptLanguage SBScriptObject::GetLanguage() const { + LLDB_INSTRUMENT_VA(this); + + return m_opaque_up ? m_opaque_up->GetLanguage() : eScriptLanguageNone; +} + +ScriptObject &SBScriptObject::ref() { + if (m_opaque_up == nullptr) + m_opaque_up = std::make_unique(nullptr, eScriptLanguageNone); + return *m_opaque_up; +} + +const ScriptObject &SBScriptObject::ref() const { + // This object should already have checked with "IsValid()" prior to calling + // this function. In case you didn't we will assert and die to let you know. + assert(m_opaque_up.get()); + return *m_opaque_up; +} + +ScriptObject *SBScriptObject::get() { return m_opaque_up.get(); } diff --git a/lldb/source/API/SBStructuredData.cpp b/lldb/source/API/SBStructuredData.cpp --- a/lldb/source/API/SBStructuredData.cpp +++ b/lldb/source/API/SBStructuredData.cpp @@ -7,13 +7,17 @@ //===----------------------------------------------------------------------===// #include "lldb/API/SBStructuredData.h" -#include "lldb/Core/StructuredDataImpl.h" -#include "lldb/Utility/Instrumentation.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBScriptObject.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/StructuredDataPlugin.h" #include "lldb/Utility/Event.h" +#include "lldb/Utility/Instrumentation.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" @@ -34,6 +38,25 @@ LLDB_INSTRUMENT_VA(this, rhs); } +SBStructuredData::SBStructuredData(const lldb::SBScriptObject obj, + const lldb::SBDebugger &debugger) { + LLDB_INSTRUMENT_VA(this, obj, debugger); + + if (!obj.IsValid()) + return; + + ScriptInterpreter *interpreter = + debugger.m_opaque_sp->GetScriptInterpreter(true, obj.GetLanguage()); + + if (!interpreter) + return; + + StructuredDataImplUP impl_up = std::make_unique( + interpreter->CreateStructuredDataFromScriptObject(obj.ref())); + if (impl_up && impl_up->IsValid()) + m_impl_up.reset(impl_up.release()); +} + SBStructuredData::SBStructuredData(const lldb::EventSP &event_sp) : m_impl_up(new StructuredDataImpl(event_sp)) { LLDB_INSTRUMENT_VA(this, event_sp); @@ -198,3 +221,9 @@ return m_impl_up->GetStringValue(dst, dst_len); } + +lldb::SBScriptObject SBStructuredData::GetGenericValue() const { + LLDB_INSTRUMENT_VA(this); + + return {m_impl_up->GetGenericValue(), eScriptLanguageDefault}; +} diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -343,6 +343,15 @@ return python::Take(obj); } + llvm::Expected GetType() const { + if (!m_py_obj) + return nullDeref(); + PyObject *obj = PyObject_Type(m_py_obj); + if (!obj) + return exception(); + return python::Take(obj); + } + llvm::Expected IsTrue() { if (!m_py_obj) return nullDeref(); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -1519,6 +1519,17 @@ return std::make_unique(*this); } +StructuredData::ObjectSP +ScriptInterpreterPythonImpl::CreateStructuredDataFromScriptObject( + ScriptObject obj) { + void *ptr = const_cast(obj.GetPointer()); + PythonObject py_obj(PyRefType::Borrowed, static_cast(ptr)); + if (!py_obj.IsValid() || py_obj.IsNone()) + return {}; + Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); + return py_obj.CreateStructuredObject(); +} + StructuredData::GenericSP ScriptInterpreterPythonImpl::OSPlugin_CreatePluginObject( const char *class_name, lldb::ProcessSP process_sp) { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -83,6 +83,9 @@ std::string &error_str, lldb::ThreadPlanSP thread_plan) override; + StructuredData::ObjectSP + CreateStructuredDataFromScriptObject(ScriptObject obj) override; + bool ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) override; diff --git a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py --- a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py +++ b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py @@ -76,6 +76,40 @@ # Tests for array data type self.array_struct_test(dict_struct) + s.Clear() + self.assertSuccess(example.GetAsJSON(s)) + py_obj = json.loads(s.GetData()) + self.assertTrue(py_obj) + self.assertIn("key_dict", py_obj) + + py_dict = py_obj["key_dict"] + self.assertEqual(py_dict["key_string"], "STRING") + self.assertEqual(py_dict["key_uint"], 0xFFFFFFFF00000000) + self.assertEqual(py_dict["key_sint"], -42) + self.assertEqual(py_dict["key_float"], 2.99) + self.assertEqual(py_dict["key_bool"], True) + self.assertEqual(py_dict["key_array"], ["23", "arr"]) + + class MyRandomClass: + payload = "foo" + + py_dict["key_generic"] = MyRandomClass() + + stp = lldb.SBScriptObject(py_dict, lldb.eScriptLanguagePython) + self.assertEqual(stp.ptr, py_dict) + + sd = lldb.SBStructuredData(stp, self.dbg) + self.assertTrue(sd.IsValid()) + self.assertEqual(sd.GetSize(), len(py_dict)) + + generic_sd = sd.GetValueForKey("key_generic") + self.assertTrue(generic_sd.IsValid()) + self.assertEqual(generic_sd.GetType(), lldb.eStructuredDataTypeGeneric) + + my_random_class = generic_sd.GetGenericValue() + self.assertTrue(my_random_class) + self.assertEqual(my_random_class.payload, MyRandomClass.payload) + def invalid_struct_test(self, example): invalid_struct = lldb.SBStructuredData() invalid_struct = example.GetValueForKey("invalid_key")