| //===-- ScriptedPythonInterface.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-python.h" |
| |
| #include "API/ScriptInterpreterBridge.h" |
| #include "lldb/API/SBValue.h" |
| #include "lldb/API/SBValueList.h" |
| #include "lldb/Host/Config.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/lldb-enumerations.h" |
| |
| #include "../ScriptInterpreterPythonImpl.h" |
| #include "ScriptedPythonInterface.h" |
| #include "lldb/Symbol/SymbolContext.h" |
| #include "lldb/ValueObject/ValueObjectList.h" |
| #include <optional> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| ScriptedPythonInterface::ScriptedPythonInterface( |
| ScriptInterpreterPythonImpl &interpreter) |
| : ScriptedInterface(), m_interpreter(interpreter) {} |
| |
| template <> |
| StructuredData::ArraySP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( |
| python::PythonObject &p, Status &error) { |
| python::PythonList result_list(python::PyRefType::Borrowed, p.get()); |
| return result_list.CreateStructuredArray(); |
| } |
| |
| template <> |
| StructuredData::DictionarySP |
| ScriptedPythonInterface::ExtractValueFromPythonObject< |
| StructuredData::DictionarySP>(python::PythonObject &p, Status &error) { |
| python::PythonDictionary result_dict(python::PyRefType::Borrowed, p.get()); |
| return result_dict.CreateStructuredDictionary(); |
| } |
| |
| template <> |
| Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( |
| python::PythonObject &p, Status &error) { |
| if (lldb::SBError *sb_error = reinterpret_cast<lldb::SBError *>( |
| python::LLDBSWIGPython_CastPyObjectToSBError(p.get()))) |
| return ScriptInterpreterBridge::GetStatus(*sb_error); |
| error = |
| Status::FromErrorString("Couldn't cast lldb::SBError to lldb::Status."); |
| |
| return {}; |
| } |
| |
| template <> |
| Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>( |
| python::PythonObject &p, Status &error) { |
| if (lldb::SBEvent *sb_event = reinterpret_cast<lldb::SBEvent *>( |
| python::LLDBSWIGPython_CastPyObjectToSBEvent(p.get()))) |
| return ScriptInterpreterBridge::GetEvent(*sb_event); |
| error = Status::FromErrorString( |
| "Couldn't cast lldb::SBEvent to lldb_private::Event."); |
| |
| return nullptr; |
| } |
| |
| template <> |
| CommandReturnObject * |
| ScriptedPythonInterface::ExtractValueFromPythonObject<CommandReturnObject *>( |
| python::PythonObject &p, Status &error) { |
| if (lldb::SBCommandReturnObject *sb_cmd_retobj = |
| reinterpret_cast<lldb::SBCommandReturnObject *>( |
| python::LLDBSWIGPython_CastPyObjectToSBCommandReturnObject( |
| p.get()))) |
| return ScriptInterpreterBridge::GetCommandReturnObject(*sb_cmd_retobj); |
| error = |
| Status::FromErrorString("couldn't cast lldb::SBCommandReturnObject to " |
| "lldb_private::CommandReturnObject."); |
| return nullptr; |
| } |
| |
| template <> |
| lldb::StreamSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>( |
| python::PythonObject &p, Status &error) { |
| if (lldb::SBStream *sb_stream = reinterpret_cast<lldb::SBStream *>( |
| python::LLDBSWIGPython_CastPyObjectToSBStream(p.get()))) |
| return ScriptInterpreterBridge::GetStream(*sb_stream); |
| error = Status::FromErrorString( |
| "Couldn't cast lldb::SBStream to lldb_private::Stream."); |
| |
| return nullptr; |
| } |
| |
| template <> |
| lldb::StackFrameSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>( |
| python::PythonObject &p, Status &error) { |
| if (lldb::SBFrame *sb_frame = reinterpret_cast<lldb::SBFrame *>( |
| python::LLDBSWIGPython_CastPyObjectToSBFrame(p.get()))) |
| return ScriptInterpreterBridge::GetStackFrame(*sb_frame); |
| error = Status::FromErrorString( |
| "Couldn't cast lldb::SBFrame to lldb_private::StackFrame."); |
| |
| return nullptr; |
| } |
| |
| template <> |
| lldb::ThreadSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ThreadSP>( |
| python::PythonObject &p, Status &error) { |
| if (lldb::SBThread *sb_thread = reinterpret_cast<lldb::SBThread *>( |
| python::LLDBSWIGPython_CastPyObjectToSBThread(p.get()))) |
| return ScriptInterpreterBridge::GetThread(*sb_thread); |
| error = Status::FromErrorString( |
| "Couldn't cast lldb::SBThread to lldb_private::Thread."); |
| |
| return nullptr; |
| } |
| |
| template <> |
| SymbolContext |
| ScriptedPythonInterface::ExtractValueFromPythonObject<SymbolContext>( |
| python::PythonObject &p, Status &error) { |
| if (lldb::SBSymbolContext *sb_symbol_context = |
| reinterpret_cast<lldb::SBSymbolContext *>( |
| python::LLDBSWIGPython_CastPyObjectToSBSymbolContext(p.get()))) |
| return ScriptInterpreterBridge::GetSymbolContext(*sb_symbol_context); |
| error = Status::FromErrorString( |
| "Couldn't cast lldb::SBSymbolContext to lldb_private::SymbolContext."); |
| |
| return {}; |
| } |
| |
| template <> |
| lldb::DataExtractorSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( |
| python::PythonObject &p, Status &error) { |
| lldb::SBData *sb_data = reinterpret_cast<lldb::SBData *>( |
| python::LLDBSWIGPython_CastPyObjectToSBData(p.get())); |
| |
| if (!sb_data) { |
| error = Status::FromErrorStringWithFormat( |
| "Couldn't cast lldb::SBData to lldb::DataExtractorSP."); |
| return nullptr; |
| } |
| |
| return ScriptInterpreterBridge::GetDataExtractor(*sb_data); |
| } |
| |
| template <> |
| lldb::BreakpointSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>( |
| python::PythonObject &p, Status &error) { |
| lldb::SBBreakpoint *sb_breakpoint = reinterpret_cast<lldb::SBBreakpoint *>( |
| python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(p.get())); |
| |
| if (!sb_breakpoint) { |
| error = Status::FromErrorStringWithFormat( |
| "Couldn't cast lldb::SBBreakpoint to lldb::BreakpointSP."); |
| return nullptr; |
| } |
| |
| return ScriptInterpreterBridge::GetBreakpoint(*sb_breakpoint); |
| } |
| |
| template <> |
| lldb::BreakpointLocationSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject< |
| lldb::BreakpointLocationSP>(python::PythonObject &p, Status &error) { |
| lldb::SBBreakpointLocation *sb_break_loc = |
| reinterpret_cast<lldb::SBBreakpointLocation *>( |
| python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(p.get())); |
| |
| if (!sb_break_loc) { |
| error = Status::FromErrorStringWithFormat( |
| "Couldn't cast lldb::SBBreakpointLocation to " |
| "lldb::BreakpointLocationSP."); |
| return nullptr; |
| } |
| |
| return ScriptInterpreterBridge::GetBreakpointLocation(*sb_break_loc); |
| } |
| |
| template <> |
| lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< |
| lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error) { |
| lldb::SBAttachInfo *sb_attach_info = reinterpret_cast<lldb::SBAttachInfo *>( |
| python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(p.get())); |
| |
| if (!sb_attach_info) { |
| error = Status::FromErrorStringWithFormat( |
| "Couldn't cast lldb::SBAttachInfo to lldb::ProcessAttachInfoSP."); |
| return nullptr; |
| } |
| |
| return ScriptInterpreterBridge::GetProcessAttachInfo(*sb_attach_info); |
| } |
| |
| template <> |
| lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< |
| lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error) { |
| lldb::SBLaunchInfo *sb_launch_info = reinterpret_cast<lldb::SBLaunchInfo *>( |
| python::LLDBSWIGPython_CastPyObjectToSBLaunchInfo(p.get())); |
| |
| if (!sb_launch_info) { |
| error = Status::FromErrorStringWithFormat( |
| "Couldn't cast lldb::SBLaunchInfo to lldb::ProcessLaunchInfoSP."); |
| return nullptr; |
| } |
| |
| return ScriptInterpreterBridge::GetProcessLaunchInfo(*sb_launch_info); |
| } |
| |
| template <> |
| lldb::ThreadPlanSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ThreadPlanSP>( |
| python::PythonObject &p, Status &error) { |
| lldb::SBThreadPlan *sb_thread_plan = reinterpret_cast<lldb::SBThreadPlan *>( |
| python::LLDBSWIGPython_CastPyObjectToSBThreadPlan(p.get())); |
| |
| if (!sb_thread_plan) { |
| error = Status::FromErrorStringWithFormat( |
| "Couldn't cast lldb::SBThreadPlan to lldb::ThreadPlanSP."); |
| return {}; |
| } |
| |
| return ScriptInterpreterBridge::GetThreadPlan(*sb_thread_plan); |
| } |
| |
| template <> |
| std::optional<MemoryRegionInfo> |
| ScriptedPythonInterface::ExtractValueFromPythonObject< |
| std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error) { |
| |
| lldb::SBMemoryRegionInfo *sb_mem_reg_info = |
| reinterpret_cast<lldb::SBMemoryRegionInfo *>( |
| python::LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(p.get())); |
| |
| if (!sb_mem_reg_info) { |
| error = Status::FromErrorStringWithFormat( |
| "Couldn't cast lldb::SBMemoryRegionInfo to " |
| "lldb_private::MemoryRegionInfo."); |
| return {}; |
| } |
| |
| return ScriptInterpreterBridge::GetMemoryRegionInfo(*sb_mem_reg_info); |
| } |
| |
| template <> |
| lldb::ExecutionContextRefSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject< |
| lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error) { |
| |
| lldb::SBExecutionContext *sb_exe_ctx = |
| reinterpret_cast<lldb::SBExecutionContext *>( |
| python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(p.get())); |
| |
| if (!sb_exe_ctx) { |
| error = Status::FromErrorStringWithFormat( |
| "Couldn't cast lldb::SBExecutionContext to " |
| "lldb::ExecutionContextRefSP."); |
| return {}; |
| } |
| |
| return ScriptInterpreterBridge::GetExecutionContextRef(*sb_exe_ctx); |
| } |
| |
| template <> |
| lldb::DescriptionLevel |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DescriptionLevel>( |
| python::PythonObject &p, Status &error) { |
| lldb::DescriptionLevel ret_val = lldb::eDescriptionLevelBrief; |
| llvm::Expected<unsigned long long> unsigned_or_err = p.AsUnsignedLongLong(); |
| if (!unsigned_or_err) { |
| error = (Status::FromError(unsigned_or_err.takeError())); |
| return ret_val; |
| } |
| unsigned long long unsigned_val = *unsigned_or_err; |
| if (unsigned_val >= lldb::DescriptionLevel::kNumDescriptionLevels) { |
| error = Status("value too large for lldb::DescriptionLevel."); |
| return ret_val; |
| } |
| return static_cast<lldb::DescriptionLevel>(unsigned_val); |
| } |
| |
| template <> |
| lldb::StackFrameListSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameListSP>( |
| python::PythonObject &p, Status &error) { |
| |
| lldb::SBFrameList *sb_frame_list = reinterpret_cast<lldb::SBFrameList *>( |
| python::LLDBSWIGPython_CastPyObjectToSBFrameList(p.get())); |
| |
| if (!sb_frame_list) { |
| error = Status::FromErrorStringWithFormat( |
| "couldn't cast lldb::SBFrameList to lldb::StackFrameListSP."); |
| return {}; |
| } |
| |
| return ScriptInterpreterBridge::GetStackFrameList(*sb_frame_list); |
| } |
| |
| template <> |
| lldb::ValueObjectSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectSP>( |
| python::PythonObject &p, Status &error) { |
| lldb::SBValue *sb_value = reinterpret_cast<lldb::SBValue *>( |
| python::LLDBSWIGPython_CastPyObjectToSBValue(p.get())); |
| if (!sb_value) { |
| error = Status::FromErrorStringWithFormat( |
| "couldn't cast lldb::SBValue to lldb::ValueObjectSP"); |
| return {}; |
| } |
| |
| return ScriptInterpreterBridge::GetValueObject(*sb_value); |
| } |
| |
| template <> |
| lldb::TargetSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::TargetSP>( |
| python::PythonObject &p, Status &error) { |
| lldb::SBTarget *sb_target = reinterpret_cast<lldb::SBTarget *>( |
| python::LLDBSWIGPython_CastPyObjectToSBTarget(p.get())); |
| if (!sb_target) { |
| error = Status::FromErrorStringWithFormat( |
| "couldn't cast lldb::SBTarget to lldb::TargetSP"); |
| return {}; |
| } |
| |
| return ScriptInterpreterBridge::GetTarget(*sb_target); |
| } |
| |
| template <> |
| lldb::ValueObjectListSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectListSP>( |
| python::PythonObject &p, Status &error) { |
| // Two Python return shapes are accepted here so callers can go through |
| // Dispatch<ValueObjectListSP>() uniformly: an `SBValueList` wrapper |
| // (what most extension methods return) and a plain Python `list` of |
| // `SBValue` (what `get_recognized_arguments` is documented to return). |
| lldb::ValueObjectListSP out = std::make_shared<ValueObjectList>(); |
| if (auto *sb_value_list = reinterpret_cast<lldb::SBValueList *>( |
| python::LLDBSWIGPython_CastPyObjectToSBValueList(p.get()))) { |
| for (uint32_t i = 0, e = sb_value_list->GetSize(); i < e; ++i) { |
| SBValue value = sb_value_list->GetValueAtIndex(i); |
| out->Append(ScriptInterpreterBridge::GetValueObject(value)); |
| } |
| return out; |
| } |
| |
| // Fallback: a plain Python `list` of `SBValue`. Round-trip through |
| // `CreateStructuredObject` so we don't touch the `PyList_*` C API |
| // directly; unknown-shape items surface as `StructuredData::Generic` |
| // holding their opaque `PyObject*`, which we hand back to SWIG to |
| // recover the SBValue wrapper. |
| StructuredData::ObjectSP structured = p.CreateStructuredObject(); |
| StructuredData::Array *arr = structured ? structured->GetAsArray() : nullptr; |
| if (arr) { |
| size_t index = 0; |
| bool aborted = false; |
| arr->ForEach([&](StructuredData::Object *item) { |
| StructuredData::Generic *generic = item ? item->GetAsGeneric() : nullptr; |
| if (!generic) { |
| error = Status::FromErrorStringWithFormatv( |
| "ValueObjectList item at index {0} is not a " |
| "StructuredData::Generic", |
| index); |
| aborted = true; |
| return false; |
| } |
| auto *sb_value = reinterpret_cast<lldb::SBValue *>( |
| python::LLDBSWIGPython_CastPyObjectToSBValue( |
| static_cast<PyObject *>(generic->GetValue()))); |
| if (sb_value) |
| if (auto valobj_sp = ScriptInterpreterBridge::GetValueObject(*sb_value)) |
| out->Append(valobj_sp); |
| ++index; |
| return true; |
| }); |
| if (aborted) |
| return {}; |
| return out; |
| } |
| |
| error = Status::FromErrorStringWithFormat( |
| "couldn't extract ValueObjectList from Python return value"); |
| return {}; |
| } |
| |
| template <> |
| std::optional<lldb::ValueType> |
| ScriptedPythonInterface::ExtractValueFromPythonObject< |
| std::optional<lldb::ValueType>>(python::PythonObject &p, Status &error) { |
| if (p.IsNone()) |
| return std::nullopt; |
| |
| llvm::Expected<unsigned long long> val = p.AsUnsignedLongLong(); |
| if (!val) { |
| error = Status::FromError(val.takeError()); |
| return std::nullopt; |
| } |
| unsigned long long unmasked = *val & ~kValueTypeFlagsMask; |
| unsigned long long flags = *val & kValueTypeFlagsMask; |
| if (unmasked == eValueTypeInvalid || unmasked > kLastValueType) { |
| error = Status::FromErrorStringWithFormatv( |
| "value type invalid or too large (got {0} | {1:x})", unmasked, flags); |
| return std::nullopt; |
| } |
| |
| return static_cast<ValueType>(unmasked | flags); |
| } |
| |
| template <> |
| lldb::DebuggerSP |
| ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DebuggerSP>( |
| python::PythonObject &p, Status &error) { |
| if (lldb::SBDebugger *sb_dbg = reinterpret_cast<lldb::SBDebugger *>( |
| python::LLDBSWIGPython_CastPyObjectToSBDebugger(p.get()))) |
| return ScriptInterpreterBridge::GetDebugger(*sb_dbg); |
| error = Status::FromErrorString( |
| "couldn't cast lldb::SBDebugger to lldb::DebuggerSP."); |
| return {}; |
| } |
| |
| template <> |
| std::vector<std::string> |
| ScriptedPythonInterface::ExtractValueFromPythonObject<std::vector<std::string>>( |
| python::PythonObject &p, Status &error) { |
| std::vector<std::string> result; |
| python::PythonList list(python::PyRefType::Borrowed, p.get()); |
| if (!list.IsValid()) { |
| error = Status::FromErrorString( |
| "couldn't extract std::vector<std::string>: not a Python list."); |
| return result; |
| } |
| |
| const uint32_t size = list.GetSize(); |
| result.reserve(size); |
| for (uint32_t i = 0; i < size; ++i) { |
| python::PythonString item(python::PyRefType::Borrowed, |
| list.GetItemAtIndex(i).get()); |
| if (!item.IsValid()) |
| continue; |
| result.push_back(item.GetString().str()); |
| } |
| return result; |
| } |