1 //===-- FormatterBytecode.cpp ---------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "FormatterBytecode.h"
10 #include "lldb/Utility/LLDBLog.h"
11 #include "lldb/ValueObject/ValueObject.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/Support/DataExtractor.h"
14 #include "llvm/Support/Format.h"
15 #include "llvm/Support/FormatProviders.h"
16 #include "llvm/Support/FormatVariadicDetails.h"
19 namespace lldb_private
{
21 std::string
toString(FormatterBytecode::OpCodes op
) {
23 #define DEFINE_OPCODE(OP, MNEMONIC, NAME) \
25 const char *s = MNEMONIC; \
26 return s ? s : #NAME; \
28 #include "FormatterBytecode.def"
29 #undef DEFINE_SIGNATURE
31 return llvm::utostr(op
);
34 std::string
toString(FormatterBytecode::Selectors sel
) {
36 #define DEFINE_SELECTOR(ID, NAME) \
39 #include "FormatterBytecode.def"
40 #undef DEFINE_SIGNATURE
42 return "@" + llvm::utostr(sel
);
45 std::string
toString(FormatterBytecode::Signatures sig
) {
47 #define DEFINE_SIGNATURE(ID, NAME) \
50 #include "FormatterBytecode.def"
51 #undef DEFINE_SIGNATURE
53 return llvm::utostr(sig
);
56 std::string
toString(const FormatterBytecode::DataStack
&data
) {
58 llvm::raw_string_ostream
os(s
);
60 for (auto &d
: data
) {
61 if (auto s
= std::get_if
<std::string
>(&d
))
62 os
<< '"' << *s
<< '"';
63 else if (auto u
= std::get_if
<uint64_t>(&d
))
65 else if (auto i
= std::get_if
<int64_t>(&d
))
67 else if (auto valobj
= std::get_if
<ValueObjectSP
>(&d
)) {
71 os
<< "object(" << valobj
->get()->GetValueAsCString() << ')';
72 } else if (auto type
= std::get_if
<CompilerType
>(&d
)) {
73 os
<< '(' << type
->GetTypeName(true) << ')';
74 } else if (auto sel
= std::get_if
<FormatterBytecode::Selectors
>(&d
)) {
83 namespace FormatterBytecode
{
85 /// Implement the @format function.
86 static llvm::Error
FormatImpl(DataStack
&data
) {
87 auto fmt
= data
.Pop
<std::string
>();
89 llvm::formatv_object_base::parseFormatString(fmt
, 0, false);
91 llvm::raw_string_ostream
os(s
);
92 unsigned num_args
= 0;
93 for (const auto &r
: replacements
)
94 if (r
.Type
== llvm::ReplacementType::Format
)
95 num_args
= std::max(num_args
, r
.Index
+ 1);
97 if (data
.size() < num_args
)
98 return llvm::createStringError("not enough arguments");
100 for (const auto &r
: replacements
) {
101 if (r
.Type
== llvm::ReplacementType::Literal
) {
105 using namespace llvm::support::detail
;
106 auto arg
= data
[data
.size() - num_args
+ r
.Index
];
107 auto format
= [&](format_adapter
&&adapter
) {
108 llvm::FmtAlign
Align(adapter
, r
.Where
, r
.Width
, r
.Pad
);
109 Align
.format(os
, r
.Options
);
112 if (auto s
= std::get_if
<std::string
>(&arg
))
113 format(build_format_adapter(s
->c_str()));
114 else if (auto u
= std::get_if
<uint64_t>(&arg
))
115 format(build_format_adapter(u
));
116 else if (auto i
= std::get_if
<int64_t>(&arg
))
117 format(build_format_adapter(i
));
118 else if (auto valobj
= std::get_if
<ValueObjectSP
>(&arg
)) {
120 format(build_format_adapter("null object"));
122 format(build_format_adapter(valobj
->get()->GetValueAsCString()));
123 } else if (auto type
= std::get_if
<CompilerType
>(&arg
))
124 format(build_format_adapter(type
->GetDisplayTypeName()));
125 else if (auto sel
= std::get_if
<FormatterBytecode::Selectors
>(&arg
))
126 format(build_format_adapter(toString(*sel
)));
129 return llvm::Error::success();
132 static llvm::Error
TypeCheck(llvm::ArrayRef
<DataStackElement
> data
,
135 return llvm::createStringError("not enough elements on data stack");
137 auto &elem
= data
.back();
142 if (!std::holds_alternative
<std::string
>(elem
))
143 return llvm::createStringError("expected String");
146 if (!std::holds_alternative
<uint64_t>(elem
))
147 return llvm::createStringError("expected UInt");
150 if (!std::holds_alternative
<int64_t>(elem
))
151 return llvm::createStringError("expected Int");
154 if (!std::holds_alternative
<ValueObjectSP
>(elem
))
155 return llvm::createStringError("expected Object");
158 if (!std::holds_alternative
<CompilerType
>(elem
))
159 return llvm::createStringError("expected Type");
162 if (!std::holds_alternative
<Selectors
>(elem
))
163 return llvm::createStringError("expected Selector");
166 return llvm::Error::success();
169 static llvm::Error
TypeCheck(llvm::ArrayRef
<DataStackElement
> data
,
170 DataType type1
, DataType type2
) {
171 if (auto error
= TypeCheck(data
, type2
))
173 return TypeCheck(data
.drop_back(), type1
);
176 static llvm::Error
TypeCheck(llvm::ArrayRef
<DataStackElement
> data
,
177 DataType type1
, DataType type2
, DataType type3
) {
178 if (auto error
= TypeCheck(data
, type3
))
180 return TypeCheck(data
.drop_back(1), type2
, type1
);
183 llvm::Error
Interpret(std::vector
<ControlStackElement
> &control
,
184 DataStack
&data
, Selectors sel
) {
186 return llvm::Error::success();
187 // Since the only data types are single endian and ULEBs, the
188 // endianness should not matter.
189 llvm::DataExtractor
cur_block(control
.back(), true, 64);
190 llvm::DataExtractor::Cursor
pc(0);
192 while (!control
.empty()) {
193 /// Activate the top most block from the control stack.
194 auto activate_block
= [&]() {
195 // Save the return address.
196 if (control
.size() > 1)
197 control
[control
.size() - 2] = cur_block
.getData().drop_front(pc
.tell());
198 cur_block
= llvm::DataExtractor(control
.back(), true, 64);
200 pc
= llvm::DataExtractor::Cursor(0);
203 /// Fetch the next byte in the instruction stream.
204 auto next_byte
= [&]() -> uint8_t {
205 // At the end of the current block?
206 while (pc
.tell() >= cur_block
.size() && !control
.empty()) {
207 if (control
.size() == 1) {
215 // Fetch the next instruction.
216 return cur_block
.getU8(pc
);
219 // Fetch the next opcode.
220 OpCodes opcode
= (OpCodes
)next_byte();
221 if (control
.empty() || !pc
)
222 return pc
.takeError();
224 LLDB_LOGV(GetLog(LLDBLog::DataFormatters
),
225 "[eval {0}] opcode={1}, control={2}, data={3}", toString(sel
),
226 toString(opcode
), control
.size(), toString(data
));
228 // Various shorthands to improve the readability of error handling.
229 #define TYPE_CHECK(...) \
230 if (auto error = TypeCheck(data, __VA_ARGS__)) \
233 auto error
= [&](llvm::Twine msg
) {
234 return llvm::createStringError(msg
+ "(opcode=" + toString(opcode
) + ")");
238 // Data stack manipulation.
241 data
.Push(data
.back());
249 uint64_t idx
= data
.Pop
<uint64_t>();
250 if (idx
>= data
.size())
251 return error("index out of bounds");
252 data
.Push(data
[idx
]);
256 TYPE_CHECK(Any
, Any
);
257 data
.Push(data
[data
.size() - 2]);
260 TYPE_CHECK(Any
, Any
);
261 auto x
= data
.PopAny();
262 auto y
= data
.PopAny();
268 TYPE_CHECK(Any
, Any
, Any
);
269 auto z
= data
.PopAny();
270 auto y
= data
.PopAny();
271 auto x
= data
.PopAny();
278 // Control stack manipulation.
280 uint64_t length
= cur_block
.getULEB128(pc
);
282 return pc
.takeError();
283 llvm::StringRef block
= cur_block
.getBytes(pc
, length
);
285 return pc
.takeError();
286 control
.push_back(block
);
291 if (data
.Pop
<uint64_t>() != 0) {
292 if (!cur_block
.size())
293 return error("empty control stack");
300 if (cur_block
.size() < 2)
301 return error("empty control stack");
302 if (data
.Pop
<uint64_t>() == 0)
303 control
[control
.size() - 2] = control
.back();
309 return pc
.takeError();
313 data
.Push(cur_block
.getULEB128(pc
));
316 data
.Push(cur_block
.getSLEB128(pc
));
318 case op_lit_selector
:
319 data
.Push(Selectors(cur_block
.getU8(pc
)));
321 case op_lit_string
: {
322 uint64_t length
= cur_block
.getULEB128(pc
);
323 llvm::StringRef bytes
= cur_block
.getBytes(pc
, length
);
324 data
.Push(bytes
.str());
330 int64_t val
= data
.Pop
<int64_t>();
331 memcpy(&casted
, &val
, sizeof(val
));
338 uint64_t val
= data
.Pop
<uint64_t>();
339 memcpy(&casted
, &val
, sizeof(val
));
345 data
.Push(data
.Pop
<ValueObjectSP
>() ? (uint64_t)0 : (uint64_t)1);
349 // Arithmetic, logic, etc.
350 #define BINOP_IMPL(OP, CHECK_ZERO) \
352 TYPE_CHECK(Any, Any); \
353 auto y = data.PopAny(); \
354 if (std::holds_alternative<uint64_t>(y)) { \
355 if (CHECK_ZERO && !std::get<uint64_t>(y)) \
356 return error(#OP " by zero"); \
358 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
359 } else if (std::holds_alternative<int64_t>(y)) { \
360 if (CHECK_ZERO && !std::get<int64_t>(y)) \
361 return error(#OP " by zero"); \
363 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
365 return error("unsupported data types"); \
367 #define BINOP(OP) BINOP_IMPL(OP, false)
368 #define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
385 #define SHIFTOP(OP, LEFT) \
387 TYPE_CHECK(Any, UInt); \
388 uint64_t y = data.Pop<uint64_t>(); \
390 return error("shift out of bounds"); \
391 if (std::holds_alternative<uint64_t>(data.back())) { \
392 uint64_t x = data.Pop<uint64_t>(); \
394 } else if (std::holds_alternative<int64_t>(data.back())) { \
395 int64_t x = data.Pop<int64_t>(); \
397 return error("left shift of negative value"); \
399 return error("shift out of bounds"); \
402 return error("unsupported data types"); \
420 data
.Push(~data
.Pop
<uint64_t>());
441 TYPE_CHECK(Selector
);
442 Selectors sel
= data
.Pop
<Selectors
>();
444 // Shorthand to improve readability.
445 #define POP_VALOBJ(VALOBJ) \
446 auto VALOBJ = data.Pop<ValueObjectSP>(); \
448 return error("null object");
450 auto sel_error
= [&](const char *msg
) {
451 return llvm::createStringError("{0} (opcode={1}, selector={2})", msg
,
452 toString(opcode
).c_str(),
453 toString(sel
).c_str());
460 const char *summary
= valobj
->GetSummaryAsCString();
461 data
.Push(summary
? std::string(valobj
->GetSummaryAsCString())
465 case sel_get_num_children
: {
468 auto result
= valobj
->GetNumChildren();
470 return result
.takeError();
471 data
.Push((uint64_t)*result
);
474 case sel_get_child_at_index
: {
475 TYPE_CHECK(Object
, UInt
);
476 auto index
= data
.Pop
<uint64_t>();
478 data
.Push(valobj
->GetChildAtIndex(index
));
481 case sel_get_child_with_name
: {
482 TYPE_CHECK(Object
, String
);
483 auto name
= data
.Pop
<std::string
>();
485 data
.Push(valobj
->GetChildMemberWithName(name
));
488 case sel_get_child_index
: {
489 TYPE_CHECK(Object
, String
);
490 auto name
= data
.Pop
<std::string
>();
492 data
.Push((uint64_t)valobj
->GetIndexOfChildWithName(name
));
498 // FIXME: do we need to control dynamic type resolution?
499 data
.Push(valobj
->GetTypeImpl().GetCompilerType(false));
502 case sel_get_template_argument_type
: {
503 TYPE_CHECK(Type
, UInt
);
504 auto index
= data
.Pop
<uint64_t>();
505 auto type
= data
.Pop
<CompilerType
>();
506 // FIXME: There is more code in SBType::GetTemplateArgumentType().
507 data
.Push(type
.GetTypeTemplateArgument(index
, true));
510 case sel_get_value
: {
513 data
.Push(std::string(valobj
->GetValueAsCString()));
516 case sel_get_value_as_unsigned
: {
520 uint64_t val
= valobj
->GetValueAsUnsigned(0, &success
);
523 return sel_error("failed to get value");
526 case sel_get_value_as_signed
: {
530 int64_t val
= valobj
->GetValueAsSigned(0, &success
);
533 return sel_error("failed to get value");
536 case sel_get_value_as_address
: {
540 uint64_t addr
= valobj
->GetValueAsUnsigned(0, &success
);
542 return sel_error("failed to get value");
543 if (auto process_sp
= valobj
->GetProcessSP())
544 addr
= process_sp
->FixDataAddress(addr
);
549 TYPE_CHECK(Object
, Type
);
550 auto type
= data
.Pop
<CompilerType
>();
552 data
.Push(valobj
->Cast(type
));
557 data
.Push((uint64_t)data
.Pop
<std::string
>().size());
562 if (auto error
= FormatImpl(data
))
567 return sel_error("selector not implemented");
572 return error("opcode not implemented");
574 return pc
.takeError();
576 } // namespace FormatterBytecode
578 } // namespace lldb_private