blob: 93729ed6ccc396bad38a55aa3384b547edc027a9 [file]
//===----------------------------------------------------------------------===//
//
// 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 "lldb/Core/PluginManager.h"
#include "lldb/Utility/ScriptedMetadata.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/lldb-enumerations.h"
#include "../SWIGPythonBridge.h"
#include "../ScriptInterpreterPythonImpl.h"
#include "ScriptedSyntheticChildrenPythonInterface.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::python;
using Locker = ScriptInterpreterPythonImpl::Locker;
ScriptedSyntheticChildrenPythonInterface::
ScriptedSyntheticChildrenPythonInterface(
ScriptInterpreterPythonImpl &interpreter)
: ScriptedSyntheticChildrenInterface(),
ScriptedPythonInterface(interpreter) {}
llvm::Expected<StructuredData::GenericSP>
ScriptedSyntheticChildrenPythonInterface::CreatePluginObject(
llvm::StringRef class_name, ValueObject &backend) {
if (class_name.empty())
return llvm::createStringError("empty class name");
ValueObjectSP valobj_sp = backend.GetSP();
if (!valobj_sp)
return llvm::createStringError("invalid backing value");
Locker py_lock(&m_interpreter,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN,
Locker::FreeLock | Locker::TearDownSession);
// Hand the provider's __init__ a fresh SBValue view of the backing value
// with synthetic children disabled, so introspecting it doesn't recursively
// re-enter this provider. `SetPreferSyntheticValue` lives on the SBValue's
// ValueImpl, so this override doesn't affect the caller's original view.
PythonObject val_arg =
SWIGBridge::ToSWIGWrapper(valobj_sp, /*use_synthetic=*/false);
ScriptedMetadata scripted_metadata(class_name,
StructuredData::DictionarySP());
return ScriptedPythonInterface::CreatePluginObject(
scripted_metadata, /*script_obj=*/nullptr, std::move(val_arg));
}
llvm::Expected<uint32_t>
ScriptedSyntheticChildrenPythonInterface::CalculateNumChildren(uint32_t max) {
// This interface requires no abstract methods, so a provider that doesn't
// implement `num_children` simply has no children rather than being broken.
llvm::Expected<std::optional<StructuredData::ObjectSP>> obj_or_err =
DispatchToOptional("num_children", max);
if (!obj_or_err)
return obj_or_err.takeError();
StructuredData::ObjectSP obj = obj_or_err->value_or(nullptr);
if (!obj || !obj->IsValid())
return 0;
// Cap at max in case the provider ignores the argument (e.g. defines
// `num_children(self)`) and returns an unbounded count.
return std::min<uint32_t>(obj->GetUnsignedIntegerValue(), max);
}
lldb::ValueObjectSP
ScriptedSyntheticChildrenPythonInterface::GetChildAtIndex(uint32_t idx) {
return LogAndDefault(Dispatch<lldb::ValueObjectSP>("get_child_at_index", idx),
LLVM_PRETTY_FUNCTION);
}
llvm::Expected<uint32_t>
ScriptedSyntheticChildrenPythonInterface::GetIndexOfChildWithName(
ConstString name) {
// A provider without `get_child_index` has no child of that name, which is
// a friendlier answer than "the method is missing".
llvm::Expected<std::optional<StructuredData::ObjectSP>> obj_or_err =
DispatchToOptional("get_child_index", name.GetCString());
if (!obj_or_err)
return obj_or_err.takeError();
StructuredData::ObjectSP obj = obj_or_err->value_or(nullptr);
if (!obj || !obj->IsValid())
return llvm::createStringErrorV("type has no child named '{0}'", name);
// `CreateStructuredObject` only produces a `SignedInteger` for values that
// don't fit as unsigned, i.e. negative ones; a non-negative index comes
// back as `UnsignedInteger` instead, so check the sign this way rather
// than via `GetSignedIntegerValue`, which would misread every valid index.
if (obj->GetAsSignedInteger())
return llvm::createStringErrorV("type has no child named '{0}'", name);
return static_cast<uint32_t>(obj->GetUnsignedIntegerValue());
}
lldb::ChildCacheState ScriptedSyntheticChildrenPythonInterface::Update() {
// update() is optional; a missing method means "always refetch".
StructuredData::ObjectSP obj =
LogAndDefault(Dispatch("update"), LLVM_PRETTY_FUNCTION);
if (!obj)
return lldb::eRefetch;
return obj->GetBooleanValue() ? lldb::eReuse : lldb::eRefetch;
}
bool ScriptedSyntheticChildrenPythonInterface::MightHaveChildren() {
// has_children() is optional and defaults to True when missing.
StructuredData::ObjectSP obj =
LogAndDefault(Dispatch("has_children"), LLVM_PRETTY_FUNCTION);
if (!obj)
return true;
return obj->GetBooleanValue();
}
lldb::ValueObjectSP
ScriptedSyntheticChildrenPythonInterface::GetSyntheticValue() {
return LogAndDefault(Dispatch<lldb::ValueObjectSP>("get_value"),
LLVM_PRETTY_FUNCTION);
}
ConstString ScriptedSyntheticChildrenPythonInterface::GetSyntheticTypeName() {
StructuredData::ObjectSP obj =
LogAndDefault(Dispatch("get_type_name"), LLVM_PRETTY_FUNCTION);
if (!obj)
return {};
return ConstString(obj->GetStringValue());
}
void ScriptedSyntheticChildrenPythonInterface::Initialize() {
const std::vector<llvm::StringRef> ci_usages = {
"type synthetic add -l <ClassName> <TypeName>"};
const std::vector<llvm::StringRef> api_usages = {
"SBTypeSynthetic.CreateWithClassName"};
PluginManager::RegisterPlugin(
GetPluginNameStatic(),
"Provide synthetic children for a type, used by 'type synthetic add -l'",
CreateInstance, eScriptedExtensionScriptedSyntheticChildren,
eScriptLanguagePython, {ci_usages, api_usages});
}
void ScriptedSyntheticChildrenPythonInterface::Terminate() {
PluginManager::UnregisterPlugin(CreateInstance);
}