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
31void TypeFilterImpl::AddExpressionPath(const std::string &path) {
32 bool need_add_dot = true;
33 if (path[0] == '.' || (path[0] == '-' && path[1] == '>') || path[0] == '[')
34 need_add_dot = false;
35 // add a '.' symbol to help forgetful users
36 if (!need_add_dot)
37 m_expression_paths.push_back(path);
38 else
39 m_expression_paths.push_back(std::string(".") + path);
40}
41
43 const std::string &path) {
44 if (i >= GetCount())
45 return false;
46 bool need_add_dot = true;
47 if (path[0] == '.' || (path[0] == '-' && path[1] == '>') || path[0] == '[')
48 need_add_dot = false;
49 // add a '.' symbol to help forgetful users
50 if (!need_add_dot)
51 m_expression_paths[i] = path;
52 else
53 m_expression_paths[i] = std::string(".") + path;
54 return true;
55}
56
57llvm::Expected<size_t>
59 const char *name_cstr = name.GetCString();
60 if (name_cstr) {
61 for (size_t i = 0; i < filter->GetCount(); i++) {
62 const char *expr_cstr = filter->GetExpressionPathAtIndex(i);
63 if (expr_cstr) {
64 if (*expr_cstr == '.')
65 expr_cstr++;
66 else if (*expr_cstr == '-' && *(expr_cstr + 1) == '>')
67 expr_cstr += 2;
68 }
69 if (expr_cstr) {
70 if (!::strcmp(name_cstr, expr_cstr))
71 return i;
72 }
73 }
74 }
75 return llvm::createStringErrorV("type has no child named '{0}'", name);
76}
77
79 StreamString sstr;
80 sstr.Printf("%s%s%s {\n", Cascades() ? "" : " (not cascading)",
81 SkipsPointers() ? " (skip pointers)" : "",
82 SkipsReferences() ? " (skip references)" : "");
83
84 for (size_t i = 0; i < GetCount(); i++) {
85 sstr.Printf(" %s\n", GetExpressionPathAtIndex(i));
86 }
87
88 sstr.PutCString("}");
89 return std::string(sstr.GetString());
90}
91
93
95
97 const SyntheticChildren::Flags &flags, const char *description,
99 : SyntheticChildren(flags), m_create_callback(std::move(callback)),
100 m_description(description ? description : "") {}
101
103
104bool SyntheticChildren::IsScripted() { return false; }
105
106std::string SyntheticChildren::GetDescription() { return ""; }
107
110 return nullptr;
111}
112
114 StreamString sstr;
115 sstr.Printf("%s%s%s %s", Cascades() ? "" : " (not cascading)",
116 SkipsPointers() ? " (skip pointers)" : "",
117 SkipsReferences() ? " (skip references)" : "",
118 m_description.c_str());
119
120 return std::string(sstr.GetString());
121}
122
123uint32_t
125 auto value_or_err = CalculateNumChildren(max);
126 if (value_or_err)
127 return *value_or_err;
128 LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), value_or_err.takeError(),
129 "{0}");
130 return 0;
131}
132
135 llvm::StringRef name, llvm::StringRef expression,
136 const ExecutionContext &exe_ctx) {
138 ValueObjectSP valobj_sp = m_backend.CreateChildValueObjectFromExpression(
139 name, expression, exe_ctx, options);
140 if (valobj_sp)
141 valobj_sp->SetSyntheticChildrenGenerated(true);
142 return valobj_sp;
143}
144
147 llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx,
148 CompilerType type, bool do_deref) {
149 ValueObjectSP valobj_sp = m_backend.CreateChildValueObjectFromAddress(
150 name, address, exe_ctx, type, do_deref);
151 if (valobj_sp)
152 valobj_sp->SetSyntheticChildrenGenerated(true);
153 return valobj_sp;
154}
155
157 llvm::StringRef name, const DataExtractor &data,
158 const ExecutionContext &exe_ctx, CompilerType type) {
159 ValueObjectSP valobj_sp =
160 m_backend.CreateChildValueObjectFromData(name, data, exe_ctx, type);
161 if (valobj_sp)
162 valobj_sp->SetSyntheticChildrenGenerated(true);
163 return valobj_sp;
164}
165
167 ValueObject &backend)
168 : SyntheticChildrenFrontEnd(backend), m_python_class(pclass) {
169 if (backend.GetID() == LLDB_INVALID_UID)
170 return;
171
172 TargetSP target_sp = backend.GetTargetSP();
173
174 if (!target_sp)
175 return;
176
177 ScriptInterpreter *interpreter =
178 target_sp->GetDebugger().GetScriptInterpreter();
179
180 if (!interpreter)
181 return;
182
184 if (!m_interface_sp)
185 return;
186
187 auto obj_or_err = m_interface_sp->CreatePluginObject(m_python_class, backend);
188 if (!obj_or_err) {
189 llvm::consumeError(obj_or_err.takeError());
190 m_interface_sp.reset();
191 }
192}
193
195
198 if (!m_interface_sp)
199 return lldb::ValueObjectSP();
200
201 return m_interface_sp->GetChildAtIndex(idx);
202}
203
207
208llvm::Expected<uint32_t>
210 if (!m_interface_sp)
211 return 0;
212 return m_interface_sp->CalculateNumChildren(UINT32_MAX);
213}
214
215llvm::Expected<uint32_t>
217 if (!m_interface_sp)
218 return 0;
219 return m_interface_sp->CalculateNumChildren(max);
220}
221
228
230 if (!m_interface_sp)
231 return false;
232
233 return m_interface_sp->MightHaveChildren();
234}
235
236llvm::Expected<size_t>
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 if (!m_impl.get_child_index)
460 return llvm::createStringErrorV("type has no child named '{0}'", name);
461
463 m_impl.get_child_index->getBuffer()};
465 data.emplace_back(name.GetString());
467 control, data, FormatterBytecode::sig_get_child_index, m_impl.version);
468 if (error)
469 return error;
470
471 if (data.size() == 0) {
472 char message[] = "@get_child_index returned empty data stack";
473 LLDB_LOG(GetLog(LLDBLog::DataFormatters), "{0}", message);
474 return llvm::createStringError(message);
475 }
476
477 const FormatterBytecode::DataStackElement &top = data.back();
478 if (auto *u = std::get_if<uint64_t>(&top))
479 if (*u <= SIZE_MAX)
480 return *u;
481 if (auto *i = std::get_if<int64_t>(&top)) {
482 if (*i >= 0 && static_cast<uint64_t>(*i) <= SIZE_MAX)
483 return *i;
484 return SIZE_MAX;
485 }
486 if (auto *ap = std::get_if<llvm::APSInt>(&top)) {
487 if (ap->isRepresentableByInt64()) {
488 int64_t v = ap->getExtValue();
489 if (v >= 0 && static_cast<uint64_t>(v) <= SIZE_MAX)
490 return static_cast<size_t>(v);
491 }
492 return SIZE_MAX;
493 }
494
495 return llvm::createStringError("@get_child_index returned invalid value");
496}
497
499 StreamString sstr;
500 sstr.Printf("%s%s%s Bytecode synthetic", Cascades() ? "" : " (not cascading)",
501 SkipsPointers() ? " (skip pointers)" : "",
502 SkipsReferences() ? " (skip references)" : "");
503
504 return std::string(sstr.GetString());
505}
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
FrontEnd(ValueObject &backend, SyntheticBytecodeImplementation &impl)
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
Determine the index of a named child.
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...
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
std::string GetString() const
Get the string value as a std::string.
const char * GetCString() const
Get the string value as a C string.
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.
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
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
Determine the index of a named child.
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
FrontEnd(std::string pclass, ValueObject &backend)
llvm::Expected< uint32_t > CalculateNumChildren() override
llvm::StringRef GetString() const
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(ConstString name) override
Determine the index of a named child.
bool SetExpressionPathAtIndex(size_t i, const char *path)
const char * GetExpressionPathAtIndex(size_t i) const
void AddExpressionPath(const char *path)
std::string GetDescription() override
std::vector< std::string > m_expression_paths
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.