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