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
9#include "FormatterBytecode.h"
10#include "lldb/Utility/LLDBLog.h"
11#include "lldb/ValueObject/ValueObject.h"
12#include "lldb/ValueObject/ValueObjectConstResult.h"
13#include "llvm/ADT/StringExtras.h"
14#include "llvm/Support/DataExtractor.h"
15#include "llvm/Support/Format.h"
16#include "llvm/Support/FormatProviders.h"
17#include "llvm/Support/FormatVariadicDetails.h"
18
19using namespace lldb;
20namespace lldb_private {
21
22std::string toString(FormatterBytecode::OpCodes op) {
23 switch (op) {
24#define DEFINE_OPCODE(OP, MNEMONIC, NAME) \
25 case OP: { \
26 const char *s = MNEMONIC; \
27 return s ? s : #NAME; \
28 }
29#include "FormatterBytecode.def"
30#undef DEFINE_SIGNATURE
31 }
32 return llvm::utostr(X: op);
33}
34
35std::string toString(FormatterBytecode::Selectors sel) {
36 switch (sel) {
37#define DEFINE_SELECTOR(ID, NAME) \
38 case ID: \
39 return "@" #NAME;
40#include "FormatterBytecode.def"
41#undef DEFINE_SIGNATURE
42 }
43 return "@" + llvm::utostr(X: sel);
44}
45
46std::string toString(FormatterBytecode::Signatures sig) {
47 switch (sig) {
48#define DEFINE_SIGNATURE(ID, NAME) \
49 case ID: \
50 return "@" #NAME;
51#include "FormatterBytecode.def"
52#undef DEFINE_SIGNATURE
53 }
54 return llvm::utostr(X: sig);
55}
56
57std::string toString(const FormatterBytecode::DataStack &data) {
58 std::string s;
59 llvm::raw_string_ostream os(s);
60 os << "[ ";
61 for (auto &d : data) {
62 if (auto s = std::get_if<std::string>(ptr: &d))
63 os << '"' << *s << '"';
64 else if (auto u = std::get_if<uint64_t>(ptr: &d))
65 os << *u << 'u';
66 else if (auto i = std::get_if<int64_t>(ptr: &d))
67 os << *i;
68 else if (auto valobj = std::get_if<ValueObjectSP>(ptr: &d)) {
69 if (!valobj->get())
70 os << "null";
71 else
72 os << "object(" << valobj->get()->GetValueAsCString() << ')';
73 } else if (auto type = std::get_if<CompilerType>(ptr: &d)) {
74 os << '(' << type->GetTypeName(BaseOnly: true) << ')';
75 } else if (auto sel = std::get_if<FormatterBytecode::Selectors>(ptr: &d)) {
76 os << toString(sel: *sel);
77 }
78 os << ' ';
79 }
80 os << ']';
81 return s;
82}
83
84namespace FormatterBytecode {
85
86/// Implement the @format function.
87static llvm::Error FormatImpl(DataStack &data) {
88 auto fmt = data.Pop<std::string>();
89 auto replacements =
90 llvm::formatv_object_base::parseFormatString(Fmt: fmt, NumArgs: 0, Validate: false);
91 std::string s;
92 llvm::raw_string_ostream os(s);
93 unsigned num_args = 0;
94 for (const auto &r : replacements)
95 if (r.Type == llvm::ReplacementType::Format)
96 num_args = std::max(a: num_args, b: r.Index + 1);
97
98 if (data.size() < num_args)
99 return llvm::createStringError(Fmt: "not enough arguments");
100
101 for (const auto &r : replacements) {
102 if (r.Type == llvm::ReplacementType::Literal) {
103 os << r.Spec;
104 continue;
105 }
106 using namespace llvm::support::detail;
107 auto arg = data[data.size() - num_args + r.Index];
108 auto format = [&](format_adapter &&adapter) {
109 llvm::FmtAlign Align(adapter, r.Where, r.Width, r.Pad);
110 Align.format(S&: os, Options: r.Options);
111 };
112
113 if (auto s = std::get_if<std::string>(ptr: &arg))
114 format(build_format_adapter(Item: s->c_str()));
115 else if (auto u = std::get_if<uint64_t>(ptr: &arg))
116 format(build_format_adapter(Item&: u));
117 else if (auto i = std::get_if<int64_t>(ptr: &arg))
118 format(build_format_adapter(Item&: i));
119 else if (auto valobj = std::get_if<ValueObjectSP>(ptr: &arg)) {
120 if (!valobj->get())
121 format(build_format_adapter(Item: "null object"));
122 else
123 format(build_format_adapter(Item: valobj->get()->GetValueAsCString()));
124 } else if (auto type = std::get_if<CompilerType>(ptr: &arg))
125 format(build_format_adapter(Item: type->GetDisplayTypeName()));
126 else if (auto sel = std::get_if<FormatterBytecode::Selectors>(ptr: &arg))
127 format(build_format_adapter(Item: toString(sel: *sel)));
128 }
129 data.Push(el: s);
130 return llvm::Error::success();
131}
132
133static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
134 DataType type) {
135 if (data.size() < 1)
136 return llvm::createStringError(Fmt: "not enough elements on data stack");
137
138 auto &elem = data.back();
139 switch (type) {
140 case Any:
141 break;
142 case String:
143 if (!std::holds_alternative<std::string>(v: elem))
144 return llvm::createStringError(Fmt: "expected String");
145 break;
146 case UInt:
147 if (!std::holds_alternative<uint64_t>(v: elem))
148 return llvm::createStringError(Fmt: "expected UInt");
149 break;
150 case Int:
151 if (!std::holds_alternative<int64_t>(v: elem))
152 return llvm::createStringError(Fmt: "expected Int");
153 break;
154 case Object:
155 if (!std::holds_alternative<ValueObjectSP>(v: elem))
156 return llvm::createStringError(Fmt: "expected Object");
157 break;
158 case Type:
159 if (!std::holds_alternative<CompilerType>(v: elem))
160 return llvm::createStringError(Fmt: "expected Type");
161 break;
162 case Selector:
163 if (!std::holds_alternative<Selectors>(v: elem))
164 return llvm::createStringError(Fmt: "expected Selector");
165 break;
166 }
167 return llvm::Error::success();
168}
169
170static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
171 DataType type1, DataType type2) {
172 if (auto error = TypeCheck(data, type: type2))
173 return error;
174 return TypeCheck(data: data.drop_back(), type: type1);
175}
176
177static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
178 DataType type1, DataType type2, DataType type3) {
179 if (auto error = TypeCheck(data, type: type3))
180 return error;
181 return TypeCheck(data: data.drop_back(N: 1), type1: type2, type2: type1);
182}
183
184llvm::Error Interpret(std::vector<ControlStackElement> &control,
185 DataStack &data, Selectors sel) {
186 if (control.empty())
187 return llvm::Error::success();
188 // Since the only data types are single endian and ULEBs, the
189 // endianness should not matter.
190 llvm::DataExtractor cur_block(control.back(), true, 64);
191 llvm::DataExtractor::Cursor pc(0);
192
193 while (!control.empty()) {
194 /// Activate the top most block from the control stack.
195 auto activate_block = [&]() {
196 // Save the return address.
197 if (control.size() > 1)
198 control[control.size() - 2] = cur_block.getData().drop_front(N: pc.tell());
199 cur_block = llvm::DataExtractor(control.back(), true, 64);
200 if (pc)
201 pc = llvm::DataExtractor::Cursor(0);
202 };
203
204 /// Fetch the next byte in the instruction stream.
205 auto next_byte = [&]() -> uint8_t {
206 // At the end of the current block?
207 while (pc.tell() >= cur_block.size() && !control.empty()) {
208 if (control.size() == 1) {
209 control.pop_back();
210 return 0;
211 }
212 control.pop_back();
213 activate_block();
214 }
215
216 // Fetch the next instruction.
217 return cur_block.getU8(C&: pc);
218 };
219
220 // Fetch the next opcode.
221 OpCodes opcode = (OpCodes)next_byte();
222 if (control.empty() || !pc)
223 return pc.takeError();
224
225 LLDB_LOGV(GetLog(LLDBLog::DataFormatters),
226 "[eval {0}] opcode={1}, control={2}, data={3}", toString(sel),
227 toString(opcode), control.size(), toString(data));
228
229 // Various shorthands to improve the readability of error handling.
230#define TYPE_CHECK(...) \
231 if (auto error = TypeCheck(data, __VA_ARGS__)) \
232 return error;
233
234 auto error = [&](llvm::Twine msg) {
235 return llvm::createStringError(S: msg + "(opcode=" + toString(op: opcode) + ")");
236 };
237
238 switch (opcode) {
239 // Data stack manipulation.
240 case op_dup:
241 TYPE_CHECK(Any);
242 data.Push(el: data.back());
243 continue;
244 case op_drop:
245 TYPE_CHECK(Any);
246 data.pop_back();
247 continue;
248 case op_pick: {
249 TYPE_CHECK(UInt);
250 uint64_t idx = data.Pop<uint64_t>();
251 if (idx >= data.size())
252 return error("index out of bounds");
253 data.Push(el: data[idx]);
254 continue;
255 }
256 case op_over:
257 TYPE_CHECK(Any, Any);
258 data.Push(el: data[data.size() - 2]);
259 continue;
260 case op_swap: {
261 TYPE_CHECK(Any, Any);
262 auto x = data.PopAny();
263 auto y = data.PopAny();
264 data.Push(el: x);
265 data.Push(el: y);
266 continue;
267 }
268 case op_rot: {
269 TYPE_CHECK(Any, Any, Any);
270 auto z = data.PopAny();
271 auto y = data.PopAny();
272 auto x = data.PopAny();
273 data.Push(el: z);
274 data.Push(el: x);
275 data.Push(el: y);
276 continue;
277 }
278
279 // Control stack manipulation.
280 case op_begin: {
281 uint64_t length = cur_block.getULEB128(C&: pc);
282 if (!pc)
283 return pc.takeError();
284 llvm::StringRef block = cur_block.getBytes(C&: pc, Length: length);
285 if (!pc)
286 return pc.takeError();
287 control.push_back(x: block);
288 continue;
289 }
290 case op_if:
291 TYPE_CHECK(UInt);
292 if (data.Pop<uint64_t>() != 0) {
293 if (!cur_block.size())
294 return error("empty control stack");
295 activate_block();
296 } else
297 control.pop_back();
298 continue;
299 case op_ifelse:
300 TYPE_CHECK(UInt);
301 if (cur_block.size() < 2)
302 return error("empty control stack");
303 if (data.Pop<uint64_t>() == 0)
304 control[control.size() - 2] = control.back();
305 control.pop_back();
306 activate_block();
307 continue;
308 case op_return:
309 control.clear();
310 return pc.takeError();
311
312 // Literals.
313 case op_lit_uint:
314 data.Push(el: cur_block.getULEB128(C&: pc));
315 continue;
316 case op_lit_int:
317 data.Push(el: cur_block.getSLEB128(C&: pc));
318 continue;
319 case op_lit_selector:
320 data.Push(el: Selectors(cur_block.getU8(C&: pc)));
321 continue;
322 case op_lit_string: {
323 uint64_t length = cur_block.getULEB128(C&: pc);
324 llvm::StringRef bytes = cur_block.getBytes(C&: pc, Length: length);
325 data.Push(el: bytes.str());
326 continue;
327 }
328 case op_as_uint: {
329 TYPE_CHECK(Int);
330 uint64_t casted;
331 int64_t val = data.Pop<int64_t>();
332 memcpy(dest: &casted, src: &val, n: sizeof(val));
333 data.Push(el: casted);
334 continue;
335 }
336 case op_as_int: {
337 TYPE_CHECK(UInt);
338 int64_t casted;
339 uint64_t val = data.Pop<uint64_t>();
340 memcpy(dest: &casted, src: &val, n: sizeof(val));
341 data.Push(el: casted);
342 continue;
343 }
344 case op_is_null: {
345 TYPE_CHECK(Object);
346 data.Push(el: data.Pop<ValueObjectSP>() ? (uint64_t)0 : (uint64_t)1);
347 continue;
348 }
349
350 // Arithmetic, logic, etc.
351#define BINOP_IMPL(OP, CHECK_ZERO) \
352 { \
353 TYPE_CHECK(Any, Any); \
354 auto y = data.PopAny(); \
355 if (std::holds_alternative<uint64_t>(y)) { \
356 if (CHECK_ZERO && !std::get<uint64_t>(y)) \
357 return error(#OP " by zero"); \
358 TYPE_CHECK(UInt); \
359 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
360 } else if (std::holds_alternative<int64_t>(y)) { \
361 if (CHECK_ZERO && !std::get<int64_t>(y)) \
362 return error(#OP " by zero"); \
363 TYPE_CHECK(Int); \
364 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
365 } else \
366 return error("unsupported data types"); \
367 }
368#define BINOP(OP) BINOP_IMPL(OP, false)
369#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
370 case op_plus:
371 BINOP(+);
372 continue;
373 case op_minus:
374 BINOP(-);
375 continue;
376 case op_mul:
377 BINOP(*);
378 continue;
379 case op_div:
380 BINOP_CHECKZERO(/);
381 continue;
382 case op_mod:
383 BINOP_CHECKZERO(%);
384 continue;
385 case op_shl:
386#define SHIFTOP(OP, LEFT) \
387 { \
388 TYPE_CHECK(Any, UInt); \
389 uint64_t y = data.Pop<uint64_t>(); \
390 if (y > 64) \
391 return error("shift out of bounds"); \
392 if (std::holds_alternative<uint64_t>(data.back())) { \
393 uint64_t x = data.Pop<uint64_t>(); \
394 data.Push(x OP y); \
395 } else if (std::holds_alternative<int64_t>(data.back())) { \
396 int64_t x = data.Pop<int64_t>(); \
397 if (x < 0 && LEFT) \
398 return error("left shift of negative value"); \
399 if (y > 64) \
400 return error("shift out of bounds"); \
401 data.Push(x OP y); \
402 } else \
403 return error("unsupported data types"); \
404 }
405 SHIFTOP(<<, true);
406 continue;
407 case op_shr:
408 SHIFTOP(>>, false);
409 continue;
410 case op_and:
411 BINOP(&);
412 continue;
413 case op_or:
414 BINOP(|);
415 continue;
416 case op_xor:
417 BINOP(^);
418 continue;
419 case op_not:
420 TYPE_CHECK(UInt);
421 data.Push(el: ~data.Pop<uint64_t>());
422 continue;
423 case op_eq:
424 BINOP(==);
425 continue;
426 case op_neq:
427 BINOP(!=);
428 continue;
429 case op_lt:
430 BINOP(<);
431 continue;
432 case op_gt:
433 BINOP(>);
434 continue;
435 case op_le:
436 BINOP(<=);
437 continue;
438 case op_ge:
439 BINOP(>=);
440 continue;
441 case op_call: {
442 TYPE_CHECK(Selector);
443 Selectors sel = data.Pop<Selectors>();
444
445 // Shorthand to improve readability.
446#define POP_VALOBJ(VALOBJ) \
447 auto VALOBJ = data.Pop<ValueObjectSP>(); \
448 if (!VALOBJ) \
449 return error("null object");
450
451 auto sel_error = [&](const char *msg) {
452 return llvm::createStringError(Fmt: "{0} (opcode={1}, selector={2})", Vals: msg,
453 Vals: toString(op: opcode).c_str(),
454 Vals: toString(sel).c_str());
455 };
456
457 switch (sel) {
458 case sel_summary: {
459 TYPE_CHECK(Object);
460 POP_VALOBJ(valobj);
461 const char *summary = valobj->GetSummaryAsCString();
462 data.Push(el: summary ? std::string(valobj->GetSummaryAsCString())
463 : std::string());
464 break;
465 }
466 case sel_get_num_children: {
467 TYPE_CHECK(Object);
468 POP_VALOBJ(valobj);
469 auto result = valobj->GetNumChildren();
470 if (!result)
471 return result.takeError();
472 data.Push(el: (uint64_t)*result);
473 break;
474 }
475 case sel_get_child_at_index: {
476 TYPE_CHECK(Object, UInt);
477 auto index = data.Pop<uint64_t>();
478 POP_VALOBJ(valobj);
479 data.Push(el: valobj->GetChildAtIndex(idx: index));
480 break;
481 }
482 case sel_get_child_with_name: {
483 TYPE_CHECK(Object, String);
484 auto name = data.Pop<std::string>();
485 POP_VALOBJ(valobj);
486 data.Push(el: valobj->GetChildMemberWithName(name));
487 break;
488 }
489 case sel_get_child_index: {
490 TYPE_CHECK(Object, String);
491 auto name = data.Pop<std::string>();
492 POP_VALOBJ(valobj);
493 if (auto index_or_err = valobj->GetIndexOfChildWithName(name))
494 data.Push(el: (uint64_t)*index_or_err);
495 else
496 return index_or_err.takeError();
497 break;
498 }
499 case sel_get_type: {
500 TYPE_CHECK(Object);
501 POP_VALOBJ(valobj);
502 // FIXME: do we need to control dynamic type resolution?
503 data.Push(el: valobj->GetTypeImpl().GetCompilerType(prefer_dynamic: false));
504 break;
505 }
506 case sel_get_template_argument_type: {
507 TYPE_CHECK(Type, UInt);
508 auto index = data.Pop<uint64_t>();
509 auto type = data.Pop<CompilerType>();
510 // FIXME: There is more code in SBType::GetTemplateArgumentType().
511 data.Push(el: type.GetTypeTemplateArgument(idx: index, expand_pack: true));
512 break;
513 }
514 case sel_get_value: {
515 TYPE_CHECK(Object);
516 POP_VALOBJ(valobj);
517 data.Push(el: std::string(valobj->GetValueAsCString()));
518 break;
519 }
520 case sel_get_value_as_unsigned: {
521 TYPE_CHECK(Object);
522 POP_VALOBJ(valobj);
523 bool success;
524 uint64_t val = valobj->GetValueAsUnsigned(fail_value: 0, success: &success);
525 data.Push(el: val);
526 if (!success)
527 return sel_error("failed to get value");
528 break;
529 }
530 case sel_get_value_as_signed: {
531 TYPE_CHECK(Object);
532 POP_VALOBJ(valobj);
533 bool success;
534 int64_t val = valobj->GetValueAsSigned(fail_value: 0, success: &success);
535 data.Push(el: val);
536 if (!success)
537 return sel_error("failed to get value");
538 break;
539 }
540 case sel_get_value_as_address: {
541 TYPE_CHECK(Object);
542 POP_VALOBJ(valobj);
543 bool success;
544 uint64_t addr = valobj->GetValueAsUnsigned(fail_value: 0, success: &success);
545 if (!success)
546 return sel_error("failed to get value");
547 if (auto process_sp = valobj->GetProcessSP())
548 addr = process_sp->FixDataAddress(pc: addr);
549 data.Push(el: addr);
550 break;
551 }
552 case sel_cast: {
553 TYPE_CHECK(Object, Type);
554 auto type = data.Pop<CompilerType>();
555 POP_VALOBJ(valobj);
556 data.Push(el: valobj->Cast(compiler_type: type));
557 break;
558 }
559 case sel_strlen: {
560 TYPE_CHECK(String);
561 data.Push(el: (uint64_t)data.Pop<std::string>().size());
562 break;
563 }
564 case sel_fmt: {
565 TYPE_CHECK(String);
566 if (auto error = FormatImpl(data))
567 return error;
568 break;
569 }
570 default:
571 return sel_error("selector not implemented");
572 }
573 continue;
574 }
575 }
576 return error("opcode not implemented");
577 }
578 return pc.takeError();
579}
580} // namespace FormatterBytecode
581
582} // namespace lldb_private
583

source code of lldb/source/DataFormatters/FormatterBytecode.cpp