mib created this revision.
mib added reviewers: jingham, JDevlieghere, bulbazord.
mib added a project: LLDB.
Herald added a project: All.
mib requested review of this revision.
Herald added a subscriber: lldb-commits.

This patch adds the ability to pass native types from the script
interpreter to methods that use a {SB,}StructuredData argument.

To do so, this patch introduce a new `ScriptedTypedObject` struture that
holds the pointer to the script object as well as the originating script
interpreter language.

This structure allows the debugger to parse the script object and
convert it to a StructuredData object.

This patch also adds a SWIG typemap that checks the input argument to
ensure it's either an SBStructuredData object, in which case it just
passes it throught, or a python object that is NOT another SB type, to
provide some guardrails for the user.

rdar://111467140

Signed-off-by: Med Ismail Bennani <ism...@bennani.ma>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D155161

Files:
  lldb/bindings/python/python-typemaps.swig
  lldb/include/lldb/API/SBDebugger.h
  lldb/include/lldb/API/SBDefines.h
  lldb/include/lldb/API/SBStructuredData.h
  lldb/include/lldb/Core/StructuredDataImpl.h
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  lldb/source/API/SBDebugger.cpp
  lldb/source/API/SBStructuredData.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
  lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py

Index: lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
===================================================================
--- lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
+++ 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.ScriptedTypedObject(py_dict, lldb.eScriptLanguagePython)
+        self.assertEqual(stp.ptr, py_dict)
+
+        sd = self.dbg.CreateStructuredDataFromScriptObject(stp)
+        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")
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -83,6 +83,9 @@
                            std::string &error_str,
                            lldb::ThreadPlanSP thread_plan) override;
 
+  StructuredData::ObjectSP
+  CreateStructuredDataFromScriptedObject(lldb::ScriptedObject obj) override;
+
   bool ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp,
                                       Event *event,
                                       bool &script_error) override;
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1519,6 +1519,16 @@
   return std::make_unique<ScriptedProcessPythonInterface>(*this);
 }
 
+StructuredData::ObjectSP
+ScriptInterpreterPythonImpl::CreateStructuredDataFromScriptedObject(
+    lldb::ScriptedObject obj) {
+  PythonObject py_obj(PyRefType::Borrowed, static_cast<PyObject *>(obj));
+  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) {
Index: lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
@@ -343,6 +343,15 @@
     return python::Take<PythonObject>(obj);
   }
 
+  llvm::Expected<PythonObject> GetType() const {
+    if (!m_py_obj)
+      return nullDeref();
+    PyObject *obj = PyObject_Type(m_py_obj);
+    if (!obj)
+      return exception();
+    return python::Take<PythonObject>(obj);
+  }
+
   llvm::Expected<bool> IsTrue() {
     if (!m_py_obj)
       return nullDeref();
Index: lldb/source/API/SBStructuredData.cpp
===================================================================
--- lldb/source/API/SBStructuredData.cpp
+++ lldb/source/API/SBStructuredData.cpp
@@ -10,6 +10,7 @@
 #include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Utility/Instrumentation.h"
 
+#include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
 #include "lldb/Target/StructuredDataPlugin.h"
@@ -45,6 +46,12 @@
 
 SBStructuredData::~SBStructuredData() = default;
 
+SBStructuredData
+SBStructuredData::CreateFromScriptObject(const ScriptedTypedObject obj,
+                                         const lldb::SBDebugger &debugger) {
+  return debugger.CreateStructuredDataFromScriptObject(obj);
+}
+
 SBStructuredData &SBStructuredData::
 operator=(const lldb::SBStructuredData &rhs) {
   LLDB_INSTRUMENT_VA(this, rhs);
@@ -197,3 +204,9 @@
 
   return m_impl_up->GetStringValue(dst, dst_len);
 }
+
+lldb::ScriptedObject SBStructuredData::GetGenericValue() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  return m_impl_up->GetGenericValue();
+}
Index: lldb/source/API/SBDebugger.cpp
===================================================================
--- lldb/source/API/SBDebugger.cpp
+++ lldb/source/API/SBDebugger.cpp
@@ -1702,6 +1702,23 @@
   return SBTrace::LoadTraceFromFile(error, *this, trace_description_file);
 }
 
+SBStructuredData SBDebugger::CreateStructuredDataFromScriptObject(
+    const ScriptedTypedObject obj) const {
+  LLDB_INSTRUMENT_VA(this, obj);
+
+  ScriptInterpreter *interpreter =
+      m_opaque_sp->GetScriptInterpreter(true, obj.lang);
+
+  if (!interpreter)
+    return {};
+
+  StructuredDataImpl sd_impl(
+      interpreter->CreateStructuredDataFromScriptedObject(obj.ptr));
+  if (sd_impl.IsValid())
+    return SBStructuredData(sd_impl);
+  return {};
+}
+
 void SBDebugger::RequestInterrupt() {
   LLDB_INSTRUMENT_VA(this);
   
Index: lldb/include/lldb/Interpreter/ScriptInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -591,6 +591,11 @@
     return *m_scripted_platform_interface_up;
   }
 
+  virtual StructuredData::ObjectSP
+  CreateStructuredDataFromScriptedObject(void *obj) {
+    return {};
+  }
+
   lldb::DataExtractorSP
   GetDataExtractorFromSBData(const lldb::SBData &data) const;
 
Index: lldb/include/lldb/Core/StructuredDataImpl.h
===================================================================
--- lldb/include/lldb/Core/StructuredDataImpl.h
+++ 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:
Index: lldb/include/lldb/API/SBStructuredData.h
===================================================================
--- lldb/include/lldb/API/SBStructuredData.h
+++ lldb/include/lldb/API/SBStructuredData.h
@@ -31,6 +31,10 @@
 
   ~SBStructuredData();
 
+  static SBStructuredData
+  CreateFromScriptObject(const ScriptedTypedObject obj,
+                         const lldb::SBDebugger &debugger);
+
   lldb::SBStructuredData &operator=(const lldb::SBStructuredData &rhs);
 
   explicit operator bool() const;
@@ -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::ScriptedObject GetGenericValue() const;
+
 protected:
   friend class SBAttachInfo;
   friend class SBLaunchInfo;
Index: lldb/include/lldb/API/SBDefines.h
===================================================================
--- lldb/include/lldb/API/SBDefines.h
+++ lldb/include/lldb/API/SBDefines.h
@@ -127,6 +127,14 @@
                                           void *baton);
 
 typedef void *ScriptedObject;
+
+typedef struct ScriptedTypedObject {
+  ScriptedTypedObject(ScriptedObject ptr, lldb::ScriptLanguage lang)
+      : ptr(ptr), lang(lang) {}
+
+  ScriptedObject ptr;
+  lldb::ScriptLanguage lang;
+} ScriptedTypedObject;
 }
 
 #endif // LLDB_API_SBDEFINES_H
Index: lldb/include/lldb/API/SBDebugger.h
===================================================================
--- lldb/include/lldb/API/SBDebugger.h
+++ lldb/include/lldb/API/SBDebugger.h
@@ -472,6 +472,9 @@
   SBTrace LoadTraceFromFile(SBError &error,
                             const SBFileSpec &trace_description_file);
 
+  SBStructuredData
+  CreateStructuredDataFromScriptObject(const ScriptedTypedObject obj) const;
+
 protected:
   friend class lldb_private::CommandPluginInterfaceImplementation;
   friend class lldb_private::python::SWIGBridge;
Index: lldb/bindings/python/python-typemaps.swig
===================================================================
--- lldb/bindings/python/python-typemaps.swig
+++ lldb/bindings/python/python-typemaps.swig
@@ -59,6 +59,58 @@
   free((char *) $1);
 }
 
+%typemap(in) lldb::ScriptedObject {
+  if ($input == Py_None) {
+    $1 = NULL;
+  } else {
+    PythonObject obj(PyRefType::Borrowed, $input);
+    if (!obj.IsValid()) {
+      PyErr_SetString(PyExc_TypeError, "Scripted object is not valid");
+      SWIG_fail;
+    }
+
+    auto lldb_module = PythonModule::Import("lldb");
+    if (!lldb_module) {
+      llvm::StringRef err_msg = llvm::toString(lldb_module.takeError());
+      PyErr_SetString(PyExc_TypeError, err_msg.data());
+      SWIG_fail;
+    }
+
+    auto sb_structured_data_class = lldb_module.get().Get("SBStructuredData");
+    if (!sb_structured_data_class) {
+      llvm::StringRef err_msg = llvm::toString(sb_structured_data_class.takeError());
+      PyErr_SetString(PyExc_TypeError, err_msg.data());
+      SWIG_fail;
+    }
+
+    if (obj.IsInstance(sb_structured_data_class.get())) {
+      $1 = $input;
+    } else {
+        auto type = obj.GetType();
+        if (!type) {
+          llvm::StringRef err_msg = llvm::toString(type.takeError());
+          PyErr_SetString(PyExc_TypeError, err_msg.data());
+          SWIG_fail;
+        }
+
+        auto type_name = As<std::string>(type.get().GetAttribute("__name__"));
+        if (!type_name) {
+          llvm::StringRef err_msg = llvm::toString(type_name.takeError());
+          PyErr_SetString(PyExc_TypeError, err_msg.data());
+          SWIG_fail;
+        }
+
+        if (llvm::StringRef(type_name.get()).startswith("SB")) {
+          // TODO: Add type name to error string.
+          PyErr_SetString(PyExc_TypeError, "input type is invalid");
+          SWIG_fail;
+        } else {
+          $1 = $input;
+        }
+    }
+  }
+}
+
 %typemap(out) lldb::ScriptedObject {
   $result = nullptr;
   if (const void* impl = $1)
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to