LLDB mainline
FormatterBytecode.cpp
Go to the documentation of this file.
1//===-- FormatterBytecode.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
14#include "lldb/lldb-forward.h"
15#include "llvm/ADT/APSInt.h"
16#include "llvm/ADT/SmallPtrSet.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/Support/DataExtractor.h"
20#include "llvm/Support/Error.h"
21#include "llvm/Support/ErrorExtras.h"
22#include "llvm/Support/Format.h"
23#include "llvm/Support/FormatProviders.h"
24#include "llvm/Support/FormatVariadicDetails.h"
25#include <type_traits>
26
27using namespace lldb;
28namespace lldb_private {
29
31 switch (op) {
32#define DEFINE_OPCODE(OP, MNEMONIC, NAME) \
33 case OP: { \
34 const char *s = MNEMONIC; \
35 return s ? s : #NAME; \
36 }
37#include "lldb/DataFormatters/FormatterBytecode.def"
38#undef DEFINE_OPCODE
39 }
40 return llvm::utostr(op);
41}
42
44 switch (sel) {
45#define DEFINE_SELECTOR(ID, NAME) \
46 case ID: \
47 return "@" #NAME;
48#include "lldb/DataFormatters/FormatterBytecode.def"
49#undef DEFINE_SELECTOR
50 }
51 return "@" + llvm::utostr(sel);
52}
53
55 switch (sig) {
56#define DEFINE_SIGNATURE(ID, NAME) \
57 case ID: \
58 return "@" #NAME;
59#include "lldb/DataFormatters/FormatterBytecode.def"
60#undef DEFINE_SIGNATURE
61 }
62 return llvm::utostr(sig);
63}
64
65std::string toString(const FormatterBytecode::DataStack &data) {
66 std::string s;
67 llvm::raw_string_ostream os(s);
68 os << "[ ";
69 for (auto &d : data) {
70 if (auto s = std::get_if<std::string>(&d))
71 os << '"' << *s << '"';
72 else if (auto u = std::get_if<uint64_t>(&d))
73 os << *u << 'u';
74 else if (auto i = std::get_if<int64_t>(&d))
75 os << *i;
76 else if (auto ap = std::get_if<llvm::APSInt>(&d))
77 os << *ap;
78 else if (auto valobj = std::get_if<ValueObjectSP>(&d)) {
79 if (!valobj->get())
80 os << "null";
81 else
82 os << "object(" << valobj->get()->GetValueAsCString() << ')';
83 } else if (auto type = std::get_if<CompilerType>(&d)) {
84 os << '(' << type->GetTypeName(true) << ')';
85 } else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&d)) {
86 os << toString(*sel);
87 } else if (auto *dict =
88 std::get_if<std::shared_ptr<FormatterBytecode::Dictionary>>(
89 &d)) {
90 os << "dict(" << (*dict ? (*dict)->size() : 0) << ')';
91 }
92 os << ' ';
93 }
94 os << ']';
95 return s;
96}
97
98namespace FormatterBytecode {
99
100/// Implement the @format function.
101static llvm::Error FormatImpl(DataStack &data) {
102 auto fmt = data.Pop<std::string>();
103 auto replacements =
104 llvm::formatv_object_base::parseFormatString(fmt, 0, false);
105 std::string s;
106 llvm::raw_string_ostream os(s);
107 unsigned num_args = 0;
108 for (const auto &r : replacements)
109 if (r.Type == llvm::ReplacementType::Format)
110 num_args = std::max(num_args, r.Index + 1);
111
112 if (data.size() < num_args)
113 return llvm::createStringError("not enough arguments");
114
115 for (const auto &r : replacements) {
116 if (r.Type == llvm::ReplacementType::Literal) {
117 os << r.Spec;
118 continue;
119 }
120 using namespace llvm::support::detail;
121 auto arg = data[data.size() - num_args + r.Index];
122 auto format = [&](FormatFunctorRef &&adapter) {
123 llvm::FmtAlign Align(adapter, r.Where, r.Width, r.Pad);
124 Align.format(os, r.Options);
125 };
126
127 if (auto s = std::get_if<std::string>(&arg))
128 format(FormatFunctor(s->c_str()));
129 else if (auto u = std::get_if<uint64_t>(&arg))
130 format(FormatFunctor(u));
131 else if (auto i = std::get_if<int64_t>(&arg))
132 format(FormatFunctor(i));
133 else if (auto ap = std::get_if<llvm::APSInt>(&arg))
134 format(FormatFunctor(*ap));
135 else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) {
136 if (!valobj->get())
137 format(FormatFunctor("null object"));
138 else
139 format(FormatFunctor(valobj->get()->GetValueAsCString()));
140 } else if (auto type = std::get_if<CompilerType>(&arg))
141 format(FormatFunctor(type->GetDisplayTypeName()));
142 else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg))
143 format(FormatFunctor(toString(*sel)));
144 else if (std::holds_alternative<DictionarySP>(arg))
145 format(FormatFunctor("dict"));
146 }
147 data.Push(s);
148 return llvm::Error::success();
149}
150
151static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
152 DataType type) {
153 if (data.size() < 1)
154 return llvm::createStringError("not enough elements on data stack");
155
156 auto &elem = data.back();
157 switch (type) {
158 case Any:
159 break;
160 case String:
161 if (!std::holds_alternative<std::string>(elem))
162 return llvm::createStringError("expected String");
163 break;
164 case UInt:
165 if (!std::holds_alternative<uint64_t>(elem))
166 return llvm::createStringError("expected UInt");
167 break;
168 case Int:
169 if (!std::holds_alternative<int64_t>(elem))
170 return llvm::createStringError("expected Int");
171 break;
172 case Object:
173 if (!std::holds_alternative<ValueObjectSP>(elem))
174 return llvm::createStringError("expected Object");
175 break;
176 case Type:
177 if (!std::holds_alternative<CompilerType>(elem))
178 return llvm::createStringError("expected Type");
179 break;
180 case Selector:
181 if (!std::holds_alternative<Selectors>(elem))
182 return llvm::createStringError("expected Selector");
183 break;
184 case Integer:
185 if (!std::holds_alternative<llvm::APSInt>(elem))
186 return llvm::createStringError("expected Integer");
187 break;
188 case Dict:
189 if (!std::holds_alternative<std::shared_ptr<FormatterBytecode::Dictionary>>(
190 elem))
191 return llvm::createStringError("expected Dictionary");
192 break;
193 }
194 return llvm::Error::success();
195}
196
197static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
198 DataType type1, DataType type2) {
199 if (auto error = TypeCheck(data, type2))
200 return error;
201 return TypeCheck(data.drop_back(), type1);
202}
203
204static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
205 DataType type1, DataType type2, DataType type3) {
206 if (auto error = TypeCheck(data, type3))
207 return error;
208 return TypeCheck(data.drop_back(), type1, type2);
209}
210
211static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
212 DataType type1, DataType type2, DataType type3,
213 DataType type4) {
214 if (auto error = TypeCheck(data, type4))
215 return error;
216 return TypeCheck(data.drop_back(), type1, type2, type3);
217}
218
219/// Wrap the result of a binary operator applied to two APSInts back into a
220/// DataStackElement. Comparison operators yield bool and need bit_width/
221/// is_unsigned to construct the boolean's APSInt representation; arithmetic
222/// operators already yield a correctly-tagged APSInt and ignore them.
223template <typename T>
224static DataStackElement WrapAPSIntResult(T result, unsigned bit_width,
225 bool is_unsigned) {
226 if constexpr (std::is_same_v<T, bool>)
227 return llvm::APSInt(llvm::APInt(bit_width, result), is_unsigned);
228 else
229 return DataStackElement(std::move(result));
230}
231
232/// Returns true if `target` is transitively reachable via `from`. Likewise,
233/// returns true if they are the same dictionary. This is used to prevent memory
234/// leaks caused by retain cycles. Dictionaries can be shared by multiple
235/// parents, so each one is visited only once to prevent exponential running
236/// time.
237static bool Reaches(const Dictionary *from, const Dictionary *target) {
238 llvm::SmallPtrSet<const Dictionary *, 8> visited;
239 llvm::SmallVector<const Dictionary *, 8> worklist = {from};
240 while (!worklist.empty()) {
241 const Dictionary *dict = worklist.pop_back_val();
242 if (dict == target)
243 return true;
244 if (!visited.insert(dict).second)
245 continue;
246 for (const auto &entry : *dict)
247 if (auto *nested = std::get_if<DictionarySP>(&entry.second))
248 worklist.push_back(nested->get());
249 }
250 return false;
251}
252
253template <typename T>
255 std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>, int>;
256template <typename T>
258 std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T> &&
259 !std::is_same_v<T, bool>,
260 int>;
261
262template <typename T, EnableIfSigned<T> = 0>
263static DataStackElement MakeInt(T value, uint32_t version) {
264 if (version < 2)
265 return int64_t(value);
266 return llvm::APSInt::get(value);
267}
268
269template <typename T, EnableIfUnsigned<T> = 0>
270static DataStackElement MakeInt(T value, uint32_t version) {
271 if (version < 2)
272 return uint64_t(value);
273 unsigned width = uint64_t(value) > uint64_t(INT64_MAX) ? 65 : 64;
274 return llvm::APSInt(llvm::APInt(width, value), /*isUnsigned=*/false);
275}
276
277llvm::Error Interpret(ControlStack &control, DataStack &data, Signatures sig,
278 uint32_t version) {
279 if (control.empty())
280 return llvm::Error::success();
281 // Since the only data types are single endian and ULEBs, the
282 // endianness should not matter.
283 llvm::DataExtractor cur_block(control.back(), true);
284 llvm::DataExtractor::Cursor pc(0);
285
286 while (!control.empty()) {
287 /// Activate the top most block from the control stack.
288 auto activate_block = [&]() {
289 // Save the return address.
290 if (control.size() > 1)
291 control[control.size() - 2] = cur_block.getData().drop_front(pc.tell());
292 cur_block = llvm::DataExtractor(control.back(), true);
293 if (pc)
294 pc = llvm::DataExtractor::Cursor(0);
295 };
296
297 /// Fetch the next byte in the instruction stream.
298 auto next_byte = [&]() -> uint8_t {
299 // At the end of the current block?
300 while (pc.tell() >= cur_block.size() && !control.empty()) {
301 if (control.size() == 1) {
302 control.pop_back();
303 return 0;
304 }
305 control.pop_back();
306 activate_block();
307 }
308
309 // Fetch the next instruction.
310 return cur_block.getU8(pc);
311 };
312
313 // Fetch the next opcode.
314 OpCodes opcode = (OpCodes)next_byte();
315 if (control.empty() || !pc)
316 return pc.takeError();
317
319 "[eval {0}] opcode={1}, control={2}, data={3}",
320 toString(sig), toString(opcode), control.size(),
321 toString(data));
322
323 // Various shorthands to improve the readability of error handling.
324#define TYPE_CHECK(...) \
325 if (auto error = TypeCheck(data, __VA_ARGS__)) \
326 return error;
327
328 auto error = [&](llvm::Twine msg) {
329 return llvm::createStringError(msg + "(opcode=" + toString(opcode) + ")");
330 };
331
332 switch (opcode) {
333 // Data stack manipulation.
334 case op_dup:
336 data.Push(data.back());
337 continue;
338 case op_drop:
340 data.pop_back();
341 continue;
342 case op_pick: {
344 uint64_t idx = data.Pop<uint64_t>();
345 if (idx >= data.size())
346 return error("index out of bounds");
347 data.Push(data[idx]);
348 continue;
349 }
350 case op_over:
352 data.Push(data[data.size() - 2]);
353 continue;
354 case op_swap: {
356 auto x = data.PopAny();
357 auto y = data.PopAny();
358 data.Push(x);
359 data.Push(y);
360 continue;
361 }
362 case op_rot: {
364 auto z = data.PopAny();
365 auto y = data.PopAny();
366 auto x = data.PopAny();
367 data.Push(z);
368 data.Push(x);
369 data.Push(y);
370 continue;
371 }
372
373 // Control stack manipulation.
374 case op_begin: {
375 uint64_t length = cur_block.getULEB128(pc);
376 if (!pc)
377 return pc.takeError();
378 llvm::StringRef block = cur_block.getBytes(pc, length);
379 if (!pc)
380 return pc.takeError();
381 control.push_back(block);
382 continue;
383 }
384 case op_if: {
385 auto cond = data.PopAny();
386 bool truthy;
387 if (auto *ap = std::get_if<llvm::APSInt>(&cond))
388 truthy = !ap->isZero();
389 else if (auto *u = std::get_if<uint64_t>(&cond))
390 // Deprecated.
391 truthy = *u != 0;
392 else
393 return error("expected Integer or UInt");
394 if (truthy) {
395 if (!cur_block.size())
396 return error("empty control stack");
397 activate_block();
398 } else
399 control.pop_back();
400 continue;
401 }
402 case op_ifelse: {
403 if (cur_block.size() < 2)
404 return error("empty control stack");
405 auto cond = data.PopAny();
406 bool truthy;
407 if (auto *ap = std::get_if<llvm::APSInt>(&cond))
408 truthy = !ap->isZero();
409 else if (auto *u = std::get_if<uint64_t>(&cond))
410 // Deprecated.
411 truthy = *u != 0;
412 else
413 return error("expected Integer or UInt");
414 if (!truthy)
415 control[control.size() - 2] = control.back();
416 control.pop_back();
417 activate_block();
418 continue;
419 }
420 case op_return:
421 control.clear();
422 return pc.takeError();
423
424 // Literals.
425 case op_lit_uint:
426 data.Push(cur_block.getULEB128(pc));
427 continue;
428 case op_lit_int:
429 data.Push(cur_block.getSLEB128(pc));
430 continue;
431 case op_lit_integer:
432 data.Push(cur_block.getSLEB128APSInt(pc));
433 continue;
434 case op_lit_selector:
435 data.Push(Selectors(cur_block.getU8(pc)));
436 continue;
437 case op_lit_null:
438 data.Push(ValueObjectSP());
439 continue;
440 case op_lit_string: {
441 uint64_t length = cur_block.getULEB128(pc);
442 llvm::StringRef bytes = cur_block.getBytes(pc, length);
443 data.Push(bytes.str());
444 continue;
445 }
446 case op_as_uint: {
448 uint64_t casted;
449 int64_t val = data.Pop<int64_t>();
450 memcpy(&casted, &val, sizeof(val));
451 data.Push(casted);
452 continue;
453 }
454 case op_as_int: {
456 int64_t casted;
457 uint64_t val = data.Pop<uint64_t>();
458 memcpy(&casted, &val, sizeof(val));
459 data.Push(casted);
460 continue;
461 }
462 case op_is_null: {
464 data.Push(data.Pop<ValueObjectSP>() ? (uint64_t)0 : (uint64_t)1);
465 continue;
466 }
467
468// Arithmetic operations.
469#define BINOP_IMPL(OP, CHECK_ZERO) \
470 { \
471 TYPE_CHECK(Any, Any); \
472 auto y = data.PopAny(); \
473 if (std::holds_alternative<uint64_t>(y)) { \
474 if (CHECK_ZERO && !std::get<uint64_t>(y)) \
475 return error(#OP " by zero"); \
476 TYPE_CHECK(UInt); \
477 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
478 } else if (std::holds_alternative<int64_t>(y)) { \
479 if (CHECK_ZERO && !std::get<int64_t>(y)) \
480 return error(#OP " by zero"); \
481 TYPE_CHECK(Int); \
482 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
483 } else if (std::holds_alternative<llvm::APSInt>(y)) { \
484 TYPE_CHECK(Integer); \
485 llvm::APSInt rhs = std::get<llvm::APSInt>(y); \
486 llvm::APSInt lhs = data.Pop<llvm::APSInt>(); \
487 if (lhs.isUnsigned() || rhs.isUnsigned()) \
488 return error("unsupported unsigned value"); \
489 unsigned width = std::max(lhs.getBitWidth(), rhs.getBitWidth()); \
490 lhs = lhs.extend(width); \
491 rhs = rhs.extend(width); \
492 if (CHECK_ZERO && rhs.isZero()) \
493 return error(#OP " by zero"); \
494 data.Push(WrapAPSIntResult(lhs OP rhs, width, lhs.isUnsigned())); \
495 } else \
496 return error("unsupported data types"); \
497 }
498#define BINOP(OP) BINOP_IMPL(OP, false)
499#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
500
501// Comparison operations.
502#define CMPOP(OP) \
503 { \
504 TYPE_CHECK(Any, Any); \
505 auto y = data.PopAny(); \
506 if (std::holds_alternative<uint64_t>(y)) { \
507 TYPE_CHECK(UInt); \
508 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
509 } else if (std::holds_alternative<int64_t>(y)) { \
510 TYPE_CHECK(Int); \
511 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
512 } else if (std::holds_alternative<llvm::APSInt>(y)) { \
513 TYPE_CHECK(Integer); \
514 llvm::APSInt rhs = std::get<llvm::APSInt>(y); \
515 llvm::APSInt lhs = data.Pop<llvm::APSInt>(); \
516 if (lhs.isUnsigned() || rhs.isUnsigned()) \
517 return error("unsupported unsigned value"); \
518 unsigned width = std::max(lhs.getBitWidth(), rhs.getBitWidth()); \
519 lhs = lhs.extend(width); \
520 rhs = rhs.extend(width); \
521 data.Push(WrapAPSIntResult(lhs OP rhs, width, lhs.isUnsigned())); \
522 } else \
523 return error("unsupported data types"); \
524 }
525
526// Bitwise operations use an Integer's underlying bit pattern, not its
527// mathematical value (ie signed-ness is ignored). This means >> is always a
528// logical (zero-filling) shift, never an arithmetic shift. Mismatched bit
529// widths are implicitly zero-extended (not sign-extended).
530#define BITOP(OP) \
531 { \
532 TYPE_CHECK(Any, Any); \
533 auto y = data.PopAny(); \
534 if (std::holds_alternative<uint64_t>(y)) { \
535 TYPE_CHECK(UInt); \
536 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
537 } else if (std::holds_alternative<int64_t>(y)) { \
538 TYPE_CHECK(Int); \
539 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
540 } else if (std::holds_alternative<llvm::APSInt>(y)) { \
541 TYPE_CHECK(Integer); \
542 llvm::APSInt rhs = std::get<llvm::APSInt>(y); \
543 llvm::APSInt lhs = data.Pop<llvm::APSInt>(); \
544 unsigned width = std::max(lhs.getBitWidth(), rhs.getBitWidth()); \
545 llvm::APInt lhs_bits = \
546 static_cast<const llvm::APInt &>(lhs).zext(width); \
547 llvm::APInt rhs_bits = \
548 static_cast<const llvm::APInt &>(rhs).zext(width); \
549 llvm::APInt bits = lhs_bits OP rhs_bits; \
550 data.Push(llvm::APSInt(std::move(bits), /*isUnsigned=*/false)); \
551 } else \
552 return error("unsupported data types"); \
553 }
554
555 case op_plus:
556 BINOP(+);
557 continue;
558 case op_minus:
559 BINOP(-);
560 continue;
561 case op_mul:
562 BINOP(*);
563 continue;
564 case op_div:
566 continue;
567 case op_mod:
569 continue;
570 case op_shl:
571#define SHIFTOP(OP, LEFT) \
572 { \
573 TYPE_CHECK(Any, UInt); \
574 uint64_t y = data.Pop<uint64_t>(); \
575 if (y > 64) \
576 return error("shift out of bounds"); \
577 if (std::holds_alternative<uint64_t>(data.back())) { \
578 uint64_t x = data.Pop<uint64_t>(); \
579 data.Push(x OP y); \
580 } else if (std::holds_alternative<int64_t>(data.back())) { \
581 int64_t x = data.Pop<int64_t>(); \
582 if (x < 0 && LEFT) \
583 return error("left shift of negative value"); \
584 if (y > 64) \
585 return error("shift out of bounds"); \
586 data.Push(x OP y); \
587 } else if (std::holds_alternative<llvm::APSInt>(data.back())) { \
588 llvm::APSInt x = data.Pop<llvm::APSInt>(); \
589 if (y > x.getBitWidth()) \
590 return error("shift out of bounds"); \
591 const llvm::APInt &bits = x; \
592 llvm::APInt shifted = \
593 LEFT ? bits.shl((unsigned)y) : bits.lshr((unsigned)y); \
594 data.Push(llvm::APSInt(std::move(shifted), /*isUnsigned=*/false)); \
595 } else \
596 return error("unsupported data types"); \
597 }
598 SHIFTOP(<<, true);
599 continue;
600 case op_shr:
601 SHIFTOP(>>, false);
602 continue;
603 case op_and:
604 BITOP(&);
605 continue;
606 case op_or:
607 BITOP(|);
608 continue;
609 case op_xor:
610 BITOP(^);
611 continue;
612 case op_not: {
614 auto x = data.PopAny();
615 if (std::holds_alternative<uint64_t>(x))
616 data.Push(~std::get<uint64_t>(x));
617 else if (auto *ap = std::get_if<llvm::APSInt>(&x)) {
618 llvm::APInt bits = ~static_cast<const llvm::APInt &>(*ap);
619 data.Push(llvm::APSInt(std::move(bits), /*isUnsigned=*/false));
620 } else
621 return error("unsupported data types");
622 continue;
623 }
624 case op_eq:
625 CMPOP(==);
626 continue;
627 case op_neq:
628 CMPOP(!=);
629 continue;
630 case op_lt:
631 CMPOP(<);
632 continue;
633 case op_gt:
634 CMPOP(>);
635 continue;
636 case op_le:
637 CMPOP(<=);
638 continue;
639 case op_ge:
640 CMPOP(>=);
641 continue;
642 case op_call: {
644 Selectors sel = data.Pop<Selectors>();
645
646 // Shorthand to improve readability.
647#define POP_VALOBJ(VALOBJ) \
648 auto VALOBJ = data.Pop<ValueObjectSP>(); \
649 if (!VALOBJ) \
650 return error("null object");
651
652 auto sel_error = [&](const char *msg) {
653 return llvm::createStringErrorV("{0} (opcode={1}, selector={2})", msg,
654 toString(opcode).c_str(),
655 toString(sel).c_str());
656 };
657
658 switch (sel) {
659 case sel_summary: {
661 POP_VALOBJ(valobj);
662 const char *summary = valobj->GetSummaryAsCString();
663 data.Push(summary ? std::string(valobj->GetSummaryAsCString())
664 : std::string());
665 break;
666 }
667 case sel_get_num_children: {
669 POP_VALOBJ(valobj);
670 auto result = valobj->GetNumChildren();
671 if (!result)
672 return result.takeError();
673 data.Push(MakeInt(*result, version));
674 break;
675 }
676 case sel_get_child_at_index: {
677 uint64_t index;
678 if (version >= 2) {
680 index = data.Pop<llvm::APSInt>().getLimitedValue(UINT32_MAX);
681 } else {
683 index = data.Pop<uint64_t>();
684 }
685 POP_VALOBJ(valobj);
686 data.Push(valobj->GetChildAtIndex(index));
687 break;
688 }
689 case sel_get_child_with_name: {
691 auto name = data.Pop<std::string>();
692 POP_VALOBJ(valobj);
693 data.Push(valobj->GetChildMemberWithName(name));
694 break;
695 }
696 case sel_get_child_index: {
698 auto name = data.Pop<std::string>();
699 POP_VALOBJ(valobj);
700 if (auto index_or_err = valobj->GetIndexOfChildWithName(name))
701 data.Push(MakeInt(*index_or_err, version));
702 else
703 return index_or_err.takeError();
704 break;
705 }
706 case sel_get_parent: {
708 POP_VALOBJ(valobj);
709 auto *parent = valobj->GetParent();
710 data.Push(parent ? parent->GetSP() : ValueObjectSP());
711 break;
712 }
713 case sel_get_type: {
715 POP_VALOBJ(valobj);
716 // FIXME: do we need to control dynamic type resolution?
717 data.Push(valobj->GetTypeImpl().GetCompilerType(false));
718 break;
719 }
720 case sel_get_template_argument_type: {
721 uint64_t index;
722 if (version >= 2) {
724 index = data.Pop<llvm::APSInt>().getLimitedValue();
725 } else {
727 index = data.Pop<uint64_t>();
728 }
729 auto type = data.Pop<CompilerType>();
730 // FIXME: There is more code in SBType::GetTemplateArgumentType().
731 data.Push(type.GetTypeTemplateArgument(index, true));
732 break;
733 }
734 case sel_get_synthetic_value: {
736 POP_VALOBJ(valobj);
737 data.Push(valobj->GetSyntheticValue());
738 break;
739 }
740 case sel_get_non_synthetic_value: {
742 POP_VALOBJ(valobj);
743 data.Push(valobj->GetNonSyntheticValue());
744 break;
745 }
746 case sel_get_value: {
748 POP_VALOBJ(valobj);
749 data.Push(std::string(valobj->GetValueAsCString()));
750 break;
751 }
752 case sel_get_value_as_unsigned: {
754 POP_VALOBJ(valobj);
755 bool success;
756 uint64_t val = valobj->GetValueAsUnsigned(0, &success);
757 data.Push(MakeInt(val, version));
758 if (!success)
759 return sel_error("failed to get value");
760 break;
761 }
762 case sel_get_value_as_signed: {
764 POP_VALOBJ(valobj);
765 bool success;
766 int64_t val = valobj->GetValueAsSigned(0, &success);
767 data.Push(MakeInt(val, version));
768 if (!success)
769 return sel_error("failed to get value");
770 break;
771 }
772 case sel_get_value_as_address: {
774 POP_VALOBJ(valobj);
775 bool success;
776 uint64_t addr = valobj->GetValueAsUnsigned(0, &success);
777 if (!success)
778 return sel_error("failed to get value");
779 if (auto process_sp = valobj->GetProcessSP())
780 addr = process_sp->FixDataAddress(addr);
781 data.Push(MakeInt(addr, version));
782 break;
783 }
784 case sel_cast: {
786 auto type = data.Pop<CompilerType>();
787 POP_VALOBJ(valobj);
788 data.Push(valobj->Cast(type));
789 break;
790 }
791 case sel_clone: {
793 auto new_name = data.Pop<std::string>();
794 POP_VALOBJ(valobj);
795 data.Push(valobj->Clone(new_name));
796 break;
797 }
798 case sel_get_pointee_type: {
800 auto type = data.Pop<CompilerType>();
801 data.Push(type.GetPointeeType());
802 break;
803 }
804 case sel_get_byte_size: {
806 auto type = data.Pop<CompilerType>();
807 uint64_t size =
808 llvm::expectedToOptional(type.GetByteSize(nullptr)).value_or(0);
809 data.Push(llvm::APSInt::get(size));
810 break;
811 }
812 case sel_create_child_at_offset: {
814 auto type = data.Pop<CompilerType>();
815 auto offset = data.Pop<llvm::APSInt>().getLimitedValue(UINT32_MAX);
816 ConstString name(data.Pop<std::string>());
817 POP_VALOBJ(valobj);
818 data.Push(valobj->GetSyntheticChildAtOffset(offset, type, true, name));
819 break;
820 }
821 case sel_strlen: {
823 auto size = data.Pop<std::string>().size();
824 data.Push(MakeInt(size, version));
825 break;
826 }
827 case sel_fmt: {
829 if (auto error = FormatImpl(data))
830 return error;
831 break;
832 }
833 default:
834 return sel_error("selector not implemented");
835 }
836 continue;
837 }
838
839 // Dictionary operations.
840 case op_dict:
841 data.Push(std::make_shared<Dictionary>());
842 continue;
843 case op_dict_set: {
845 auto value = data.PopAny();
846 auto key = data.Pop<std::string>();
847 auto dict_sp = data.Pop<DictionarySP>();
848 if (auto *nested_sp = std::get_if<DictionarySP>(&value))
849 if (Reaches(nested_sp->get(), dict_sp.get()))
850 return error("dict_set would create a reference cycle");
851 (*dict_sp)[key] = std::move(value);
852 continue;
853 }
854 case op_dict_get: {
856 auto key = data.Pop<std::string>();
857 auto dict_sp = data.Pop<DictionarySP>();
858 auto it = dict_sp->find(key);
859 if (it == dict_sp->end())
860 return error("key not found in dictionary");
861 data.Push(it->second);
862 continue;
863 }
864 case op_dict_has: {
866 auto key = data.Pop<std::string>();
867 auto dict_sp = data.Pop<DictionarySP>();
868 bool found = dict_sp->find(key) != dict_sp->end();
869 data.Push(llvm::APSInt::get(found));
870 continue;
871 }
872 }
873 return error("opcode not implemented");
874 }
875 return pc.takeError();
876}
877} // namespace FormatterBytecode
878
879} // namespace lldb_private
static llvm::raw_ostream & error(Stream &strm)
#define BINOP(OP)
#define CMPOP(OP)
#define SHIFTOP(OP, LEFT)
#define BITOP(OP)
#define TYPE_CHECK(...)
#define POP_VALOBJ(VALOBJ)
#define BINOP_CHECKZERO(OP)
#define LLDB_LOG_VERBOSE(log,...)
Definition Log.h:382
Generic representation of a type in a programming language.
A uniqued constant string class.
Definition ConstString.h:40
#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
static DataStackElement MakeInt(T value, uint32_t version)
std::enable_if_t< std::is_integral_v< T > &&std::is_signed_v< T >, int > EnableIfSigned
static bool Reaches(const Dictionary *from, const Dictionary *target)
Returns true if target is transitively reachable via from.
static llvm::Error TypeCheck(llvm::ArrayRef< DataStackElement > data, DataType type)
std::shared_ptr< Dictionary > DictionarySP
static llvm::Error FormatImpl(DataStack &data)
Implement the @format function.
static DataStackElement WrapAPSIntResult(T result, unsigned bit_width, bool is_unsigned)
Wrap the result of a binary operator applied to two APSInts back into a DataStackElement.
std::enable_if_t< std::is_integral_v< T > &&std::is_unsigned_v< T > && !std::is_same_v< T, bool >, int > EnableIfUnsigned
@ Int
Deprecated: use Integer.
@ UInt
Deprecated: use Integer.
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
static uint32_t Align(uint32_t val, uint32_t alignment)
Definition ARMUtils.h:21
static uint32_t bits(const uint32_t val, const uint32_t msbit, const uint32_t lsbit)
Definition ARMUtils.h:265
std::string toString(FormatterBytecode::OpCodes op)
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP