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 | |
19 | using namespace lldb; |
20 | namespace lldb_private { |
21 | |
22 | std::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 | |
35 | std::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 | |
46 | std::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 | |
57 | std::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 | |
84 | namespace FormatterBytecode { |
85 | |
86 | /// Implement the @format function. |
87 | static 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 | |
133 | static 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 | |
170 | static 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 | |
177 | static 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 | |
184 | llvm::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 | |