LLDB mainline
TypeSynthetic.cpp
Go to the documentation of this file.
1//===-- TypeSynthetic.cpp -------------------------------------------------===//
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#include "lldb/Utility/Log.h"
12#include "lldb/lldb-forward.h"
13#include "lldb/lldb-public.h"
14
15#include "lldb/Core/Debugger.h"
22#include "lldb/Target/Target.h"
24#include "llvm/Support/Error.h"
25#include "llvm/Support/ErrorExtras.h"
26#include <cstdint>
27
28using namespace lldb;
29using namespace lldb_private;
30
31static std::string NormalizeExpressionPath(llvm::StringRef path) {
32 if (!path.starts_with('.') && !path.starts_with("->") &&
33 !path.starts_with('['))
34 return std::string(".") + path.str();
35
36 return path.str();
37}
38
39llvm::StringRef TypeFilterImpl::GetExpressionPathAtIndex(size_t i) const {
40 if (i >= GetCount())
41 return llvm::StringRef();
42
43 return m_expression_paths[i];
44}
45
46void TypeFilterImpl::AddExpressionPath(llvm::StringRef path) {
48}
49
50bool TypeFilterImpl::SetExpressionPathAtIndex(size_t i, llvm::StringRef path) {
51 if (i >= GetCount())
52 return false;
53
55 return true;
56}
57
58llvm::Expected<size_t>
60 if (!name.empty()) {
61 for (size_t i = 0; i < filter->GetCount(); i++) {
62 llvm::StringRef expr_path = filter->GetExpressionPathAtIndex(i);
63 if (!expr_path.empty()) {
64 if (expr_path.starts_with('.'))
65 expr_path = expr_path.drop_front();
66 else if (expr_path.starts_with("->"))
67 expr_path = expr_path.drop_front(2);
68
69 if (name == expr_path)
70 return i;
71 }
72 }
73 }
74 return llvm::createStringErrorV("type has no child named '{0}'", name);
75}
76
78 StreamString sstr;
79 sstr.Printf("%s%s%s {\n", Cascades() ? "" : " (not cascading)",
80 SkipsPointers() ? " (skip pointers)" : "",
81 SkipsReferences() ? " (skip references)" : "");
82
83 for (size_t i = 0; i < GetCount(); i++) {
84 sstr.Format(" {0}\n", GetExpressionPathAtIndex(i));
85 }
86
87 sstr.PutCString("}");
88 return std::string(sstr.GetString());
89}
90
92
94
96 const SyntheticChildren::Flags &flags, const char *description,
98 : SyntheticChildren(flags), m_create_callback(std::move(callback)),
99 m_description(description ? description : "") {}
100
102
103bool SyntheticChildren::IsScripted() { return false; }
104
105std::string SyntheticChildren::GetDescription() { return ""; }
106
109 return nullptr;
110}
111
113 StreamString sstr;
114 sstr.Printf("%s%s%s %s", Cascades() ? "" : " (not cascading)",
115 SkipsPointers() ? " (skip pointers)" : "",
116 SkipsReferences() ? " (skip references)" : "",
117 m_description.c_str());
118
119 return std::string(sstr.GetString());
120}
121
122uint32_t
124 auto value_or_err = CalculateNumChildren(max);
125 if (value_or_err)
126 return *value_or_err;
127 LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), value_or_err.takeError(),
128 "{0}");
129 return 0;
130}
131
134 llvm::StringRef name, llvm::StringRef expression,
135 const ExecutionContext &exe_ctx) {
137 ValueObjectSP valobj_sp = m_backend.CreateChildValueObjectFromExpression(
138 name, expression, exe_ctx, options);
139 if (valobj_sp)
140 valobj_sp->SetSyntheticChildrenGenerated(true);
141 return valobj_sp;
142}
143
146 llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx,
147 CompilerType type, bool do_deref) {
148 ValueObjectSP valobj_sp = m_backend.CreateChildValueObjectFromAddress(
149 name, address, exe_ctx, type, do_deref);
150 if (valobj_sp)
151 valobj_sp->SetSyntheticChildrenGenerated(true);
152 return valobj_sp;
153}
154
156 llvm::StringRef name, const DataExtractor &data,
157 const ExecutionContext &exe_ctx, CompilerType type) {
158 ValueObjectSP valobj_sp =
159 m_backend.CreateChildValueObjectFromData(name, data, exe_ctx, type);
160 if (valobj_sp)
161 valobj_sp->SetSyntheticChildrenGenerated(true);
162 return valobj_sp;
163}
164
166 ValueObject &backend)
167 : SyntheticChildrenFrontEnd(backend), m_python_class(pclass) {
168 if (backend.GetID() == LLDB_INVALID_UID)
169 return;
170
171 TargetSP target_sp = backend.GetTargetSP();
172
173 if (!target_sp)
174 return;
175
176 ScriptInterpreter *interpreter =
177 target_sp->GetDebugger().GetScriptInterpreter();
178
179 if (!interpreter)
180 return;
181
183 if (!m_interface_sp)
184 return;
185
186 auto obj_or_err = m_interface_sp->CreatePluginObject(m_python_class, backend);
187 if (!obj_or_err) {
188 llvm::consumeError(obj_or_err.takeError());
189 m_interface_sp.reset();
190 }
191}
192
194
197 if (!m_interface_sp)
198 return lldb::ValueObjectSP();
199
200 return m_interface_sp->GetChildAtIndex(idx);
201}
202
206
207llvm::Expected<uint32_t>
209 if (!m_interface_sp)
210 return 0;
211 return m_interface_sp->CalculateNumChildren(UINT32_MAX);
212}
213
214llvm::Expected<uint32_t>
216 if (!m_interface_sp)
217 return 0;
218 return m_interface_sp->CalculateNumChildren(max);
219}
220
227
229 if (!m_interface_sp)
230 return false;
231
232 return m_interface_sp->MightHaveChildren();
233}
234
235llvm::Expected<size_t>
237 llvm::StringRef name) {
238 if (!m_interface_sp)
239 return llvm::createStringErrorV("type has no child named '{0}'", name);
240 return m_interface_sp->GetIndexOfChildWithName(name);
241}
242
244 if (!m_interface_sp)
245 return nullptr;
246
247 return m_interface_sp->GetSyntheticValue();
248}
249
251 if (!m_interface_sp)
252 return ConstString();
253
254 return m_interface_sp->GetSyntheticTypeName();
255}
256
258 if (!m_interface_sp)
259 return nullptr;
260
261 StructuredData::GenericSP obj = m_interface_sp->GetScriptObjectInstance();
262 if (!obj)
263 return nullptr;
264
265 return obj->GetValue();
266}
267
269 StreamString sstr;
270 sstr.Printf("%s%s%s Python class %s", Cascades() ? "" : " (not cascading)",
271 SkipsPointers() ? " (skip pointers)" : "",
272 SkipsReferences() ? " (skip references)" : "",
273 m_python_class.c_str());
274
275 return std::string(sstr.GetString());
276}
277
280 : SyntheticChildrenFrontEnd(backend), m_impl(impl) {
282
284 // Version 2: `self` is a Dictionary and passed to each method.
285 if (m_impl.version >= 2) {
286 self_sp = std::make_shared<FormatterBytecode::Dictionary>();
287 data.Push(self_sp);
288 }
289
290 // Pass the "valobj" argument.
291 data.Push(backend.GetSP());
292
293 if (!m_impl.init) {
294 if (m_impl.version >= 2) {
295 // Without @init, store the Object in `self` for the other methods.
296 (*self_sp)["valobj"] = backend.GetSP();
297 m_init_results.assign({self_sp});
298 } else {
299 m_init_results = std::move(data);
300 }
301 return;
302 }
303
304 FormatterBytecode::ControlStack control = {m_impl.init->getBuffer()};
306 control, data, FormatterBytecode::sig_init, m_impl.version);
307 if (error) {
309 "@init failed: {0}");
310 return;
311 }
312
313 if (m_impl.version >= 2)
314 m_init_results.assign({self_sp});
315 else if (data.size() > 0)
316 m_init_results = std::move(data);
317}
318
320 if (!m_impl.update) {
323 }
324
325 FormatterBytecode::ControlStack control = {m_impl.update->getBuffer()};
328 control, data, FormatterBytecode::sig_update, m_impl.version);
329 if (error) {
331 "@update failed: {0}");
333 }
334
335 // Version 2: @update mutates `self` in place, and returns an int representing
336 // the ChildCacheState return value.
337 if (m_impl.version >= 2) {
339 auto *reply =
340 data.empty() ? nullptr : std::get_if<llvm::APSInt>(&data.back());
341 if (!reply || !(*reply == 0 || *reply == 1)) {
343 "@update did not reply with an Integer of 0 or 1, as required "
344 "of version 2 formatters (type: `{0}`, name: `{1}`)",
345 m_backend.GetDisplayTypeName(), m_backend.GetName());
347 }
348 return static_cast<ChildCacheState>(reply->getExtValue());
349 }
350
351 std::optional<ChildCacheState> can_reuse = std::nullopt;
352 if (!data.empty()) {
353 const FormatterBytecode::DataStackElement &top = data.back();
354 if (auto *u = std::get_if<uint64_t>(&top))
355 if (*u == 0 || *u == 1)
356 can_reuse = static_cast<ChildCacheState>(*u);
357 if (auto *i = std::get_if<int64_t>(&top))
358 if (*i == 0 || *i == 1)
359 can_reuse = static_cast<ChildCacheState>(*i);
360 if (auto *ap = std::get_if<llvm::APSInt>(&top))
361 if (*ap == 0 || *ap == 1)
362 can_reuse = static_cast<ChildCacheState>(ap->getExtValue());
363 }
364
365 if (can_reuse) {
366 data.pop_back();
367 LLDB_LOG(
369 "Bytecode formatter can reuse @update: {0} (type: `{1}`, name: `{2}`)",
370 can_reuse ? "true" : "false", m_backend.GetDisplayTypeName(),
371 m_backend.GetName());
372 } else {
374 "Bytecode formatter did not return a valid reuse response from "
375 "@update (type: `{}`, name: `{}`)",
376 m_backend.GetDisplayTypeName(), m_backend.GetName());
377 }
378
379 if (data.size() > 0)
380 m_self = std::move(data);
381
382 return can_reuse.value_or(ChildCacheState::eRefetch);
383}
384
385llvm::Expected<uint32_t>
387 if (!m_impl.num_children)
388 return 0;
389
390 FormatterBytecode::ControlStack control = {m_impl.num_children->getBuffer()};
393 control, data, FormatterBytecode::sig_get_num_children, m_impl.version);
394 if (error)
395 return error;
396
397 if (data.size() == 0) {
398 char message[] = "@get_num_children returned empty data stack";
399 LLDB_LOG(GetLog(LLDBLog::DataFormatters), "{0}", message);
400 return llvm::createStringError(message);
401 }
402
403 const FormatterBytecode::DataStackElement &top = data.back();
404 if (auto *u = std::get_if<uint64_t>(&top))
405 if (*u <= UINT32_MAX)
406 return *u;
407 if (auto *i = std::get_if<int64_t>(&top)) {
408 if (*i >= 0 && *i <= UINT32_MAX)
409 return *i;
410 return UINT32_MAX;
411 }
412 if (auto *ap = std::get_if<llvm::APSInt>(&top)) {
413 if (ap->isRepresentableByInt64()) {
414 int64_t v = ap->getExtValue();
415 if (v >= 0 && v <= UINT32_MAX)
416 return static_cast<uint32_t>(v);
417 }
418 return UINT32_MAX;
419 }
420
421 return llvm::createStringError("@get_num_children returned invalid value");
422}
423
426 if (!m_impl.get_child_at_index)
427 return {};
428
430 m_impl.get_child_at_index->getBuffer()};
432 if (m_impl.version >= 2)
433 data.Push(llvm::APSInt::get(idx));
434 else
435 data.emplace_back((uint64_t)idx);
437 control, data, FormatterBytecode::sig_get_child_at_index, m_impl.version);
438 if (error) {
440 "@get_child_at_index failed: {0}");
441 return {};
442 }
443
444 if (data.size() == 0) {
446 "@get_child_at_index returned empty data stack");
447 return {};
448 }
449
450 const FormatterBytecode::DataStackElement &top = data.back();
451 if (auto *child = std::get_if<ValueObjectSP>(&top))
452 return *child;
453
454 return {};
455}
456
457llvm::Expected<size_t>
459 llvm::StringRef name) {
460 if (!m_impl.get_child_index)
461 return llvm::createStringErrorV("type has no child named '{0}'", name);
462
464 m_impl.get_child_index->getBuffer()};
466 data.emplace_back(name.str());
468 control, data, FormatterBytecode::sig_get_child_index, m_impl.version);
469 if (error)
470 return error;
471
472 if (data.size() == 0) {
473 char message[] = "@get_child_index returned empty data stack";
474 LLDB_LOG(GetLog(LLDBLog::DataFormatters), "{0}", message);
475 return llvm::createStringError(message);
476 }
477
478 const FormatterBytecode::DataStackElement &top = data.back();
479 if (auto *u = std::get_if<uint64_t>(&top))
480 if (*u <= SIZE_MAX)
481 return *u;
482 if (auto *i = std::get_if<int64_t>(&top)) {
483 if (*i >= 0 && static_cast<uint64_t>(*i) <= SIZE_MAX)
484 return *i;
485 return SIZE_MAX;
486 }
487 if (auto *ap = std::get_if<llvm::APSInt>(&top)) {
488 if (ap->isRepresentableByInt64()) {
489 int64_t v = ap->getExtValue();
490 if (v >= 0 && static_cast<uint64_t>(v) <= SIZE_MAX)
491 return static_cast<size_t>(v);
492 }
493 return SIZE_MAX;
494 }
495
496 return llvm::createStringError("@get_child_index returned invalid value");
497}
498
500 StreamString sstr;
501 sstr.Printf("%s%s%s Bytecode synthetic", Cascades() ? "" : " (not cascading)",
502 SkipsPointers() ? " (skip pointers)" : "",
503 SkipsReferences() ? " (skip references)" : "");
504
505 return std::string(sstr.GetString());
506}
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:375
#define LLDB_LOG_ERRORV(log, error,...)
Definition Log.h:421
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
static std::string NormalizeExpressionPath(llvm::StringRef path)
FrontEnd(ValueObject &backend, SyntheticBytecodeImplementation &impl)
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
llvm::Expected< uint32_t > CalculateNumChildren() override
const SyntheticBytecodeImplementation & m_impl
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
llvm::Expected< size_t > GetIndexOfChildWithName(llvm::StringRef name) override
Determine the index of a named child.
SyntheticBytecodeImplementation m_impl
CreateFrontEndCallback m_create_callback
std::string GetDescription() override
CXXSyntheticChildren(const SyntheticChildren::Flags &flags, const char *description, CreateFrontEndCallback callback)
std::function< SyntheticChildrenFrontEnd *(CXXSyntheticChildren *, lldb::ValueObjectSP)> CreateFrontEndCallback
Generic representation of a type in a programming language.
A uniqued constant string class.
Definition ConstString.h:40
An data extractor class.
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
virtual lldb::ScriptedSyntheticChildrenInterfaceSP CreateScriptedSyntheticChildrenInterface()
Debugger & GetDebugger()
Get the debugger associated with this script interpreter.
llvm::Expected< size_t > GetIndexOfChildWithName(llvm::StringRef name) override
Determine the index of a named child.
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
lldb::ScriptedSyntheticChildrenInterfaceSP m_interface_sp
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
FrontEnd(std::string pclass, ValueObject &backend)
llvm::Expected< uint32_t > CalculateNumChildren() override
llvm::StringRef GetString() const
void Format(const char *format, Args &&... args)
Forwards the arguments to llvm::formatv and writes to the stream.
Definition Stream.h:370
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:134
size_t PutCString(llvm::StringRef cstr)
Output a C string to the stream.
Definition Stream.cpp:63
std::shared_ptr< Generic > GenericSP
uint32_t CalculateNumChildrenIgnoringErrors(uint32_t max=UINT32_MAX)
lldb::ValueObjectSP CreateChildValueObjectFromAddress(llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, CompilerType type, bool do_deref=true)
std::unique_ptr< SyntheticChildrenFrontEnd > UniquePointer
lldb::ValueObjectSP CreateChildValueObjectFromExpression(llvm::StringRef name, llvm::StringRef expression, const ExecutionContext &exe_ctx)
virtual llvm::Expected< uint32_t > CalculateNumChildren()=0
SyntheticChildrenFrontEnd(ValueObject &backend)
lldb::ValueObjectSP CreateChildValueObjectFromData(llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type)
virtual std::string GetDescription()=0
virtual SyntheticChildrenFrontEnd::UniquePointer GetFrontEnd(ValueObject &backend)=0
SyntheticChildren(const Flags &flags)
llvm::Expected< size_t > GetIndexOfChildWithName(llvm::StringRef name) override
Determine the index of a named child.
bool SetExpressionPathAtIndex(size_t i, llvm::StringRef path)
void AddExpressionPath(llvm::StringRef path)
std::string GetDescription() override
std::vector< std::string > m_expression_paths
llvm::StringRef GetExpressionPathAtIndex(size_t i) const
lldb::ValueObjectSP GetSP()
lldb::user_id_t GetID() const
Returns a unique id for this ValueObject.
lldb::TargetSP GetTargetSP() const
#define LLDB_INVALID_UID
#define UINT32_MAX
llvm::Error Interpret(ControlStack &control, DataStack &data, Signatures sig, uint32_t version)
Interpret the bytecode on control.
std::vector< ControlStackElement > ControlStack
std::variant< std::string, uint64_t, int64_t, lldb::ValueObjectSP, CompilerType, Selectors, llvm::APSInt, DictionarySP > DataStackElement
std::shared_ptr< Dictionary > DictionarySP
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
ChildCacheState
Specifies if children need to be re-computed after a call to SyntheticChildrenFrontEnd::Update.
@ eRefetch
Children need to be recomputed dynamically.
@ eReuse
Children did not change and don't need to be recomputed; re-use what we computed the last time we cal...
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
std::shared_ptr< lldb_private::Target > TargetSP
uint32_t version
The version of this record. Specs are found in formatterbytecode.md.