583 lines · cpp
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-exception6//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_SIGNATURE31 }32 return llvm::utostr(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_SIGNATURE42 }43 return "@" + llvm::utostr(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_SIGNATURE53 }54 return llvm::utostr(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>(&d))63 os << '"' << *s << '"';64 else if (auto u = std::get_if<uint64_t>(&d))65 os << *u << 'u';66 else if (auto i = std::get_if<int64_t>(&d))67 os << *i;68 else if (auto valobj = std::get_if<ValueObjectSP>(&d)) {69 if (!valobj->get())70 os << "null";71 else72 os << "object(" << valobj->get()->GetValueAsCString() << ')';73 } else if (auto type = std::get_if<CompilerType>(&d)) {74 os << '(' << type->GetTypeName(true) << ')';75 } else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&d)) {76 os << toString(*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, 0, 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(num_args, r.Index + 1);97 98 if (data.size() < num_args)99 return llvm::createStringError("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(os, r.Options);111 };112 113 if (auto s = std::get_if<std::string>(&arg))114 format(build_format_adapter(s->c_str()));115 else if (auto u = std::get_if<uint64_t>(&arg))116 format(build_format_adapter(u));117 else if (auto i = std::get_if<int64_t>(&arg))118 format(build_format_adapter(i));119 else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) {120 if (!valobj->get())121 format(build_format_adapter("null object"));122 else123 format(build_format_adapter(valobj->get()->GetValueAsCString()));124 } else if (auto type = std::get_if<CompilerType>(&arg))125 format(build_format_adapter(type->GetDisplayTypeName()));126 else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg))127 format(build_format_adapter(toString(*sel)));128 }129 data.Push(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("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>(elem))144 return llvm::createStringError("expected String");145 break;146 case UInt:147 if (!std::holds_alternative<uint64_t>(elem))148 return llvm::createStringError("expected UInt");149 break;150 case Int:151 if (!std::holds_alternative<int64_t>(elem))152 return llvm::createStringError("expected Int");153 break;154 case Object:155 if (!std::holds_alternative<ValueObjectSP>(elem))156 return llvm::createStringError("expected Object");157 break;158 case Type:159 if (!std::holds_alternative<CompilerType>(elem))160 return llvm::createStringError("expected Type");161 break;162 case Selector:163 if (!std::holds_alternative<Selectors>(elem))164 return llvm::createStringError("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, type2))173 return error;174 return TypeCheck(data.drop_back(), type1);175}176 177static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,178 DataType type1, DataType type2, DataType type3) {179 if (auto error = TypeCheck(data, type3))180 return error;181 return TypeCheck(data.drop_back(1), 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, the189 // 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(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(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(msg + "(opcode=" + toString(opcode) + ")");236 };237 238 switch (opcode) {239 // Data stack manipulation.240 case op_dup:241 TYPE_CHECK(Any);242 data.Push(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(data[idx]);254 continue;255 }256 case op_over:257 TYPE_CHECK(Any, Any);258 data.Push(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(x);265 data.Push(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(z);274 data.Push(x);275 data.Push(y);276 continue;277 }278 279 // Control stack manipulation.280 case op_begin: {281 uint64_t length = cur_block.getULEB128(pc);282 if (!pc)283 return pc.takeError();284 llvm::StringRef block = cur_block.getBytes(pc, length);285 if (!pc)286 return pc.takeError();287 control.push_back(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 } else297 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(cur_block.getULEB128(pc));315 continue;316 case op_lit_int:317 data.Push(cur_block.getSLEB128(pc));318 continue;319 case op_lit_selector:320 data.Push(Selectors(cur_block.getU8(pc)));321 continue;322 case op_lit_string: {323 uint64_t length = cur_block.getULEB128(pc);324 llvm::StringRef bytes = cur_block.getBytes(pc, length);325 data.Push(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(&casted, &val, sizeof(val));333 data.Push(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(&casted, &val, sizeof(val));341 data.Push(casted);342 continue;343 }344 case op_is_null: {345 TYPE_CHECK(Object);346 data.Push(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(~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("{0} (opcode={1}, selector={2})", msg,453 toString(opcode).c_str(),454 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(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((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(valobj->GetChildAtIndex(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(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((uint64_t)*index_or_err);495 else496 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(valobj->GetTypeImpl().GetCompilerType(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(type.GetTypeTemplateArgument(index, true));512 break;513 }514 case sel_get_value: {515 TYPE_CHECK(Object);516 POP_VALOBJ(valobj);517 data.Push(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(0, &success);525 data.Push(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(0, &success);535 data.Push(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(0, &success);545 if (!success)546 return sel_error("failed to get value");547 if (auto process_sp = valobj->GetProcessSP())548 addr = process_sp->FixDataAddress(addr);549 data.Push(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(valobj->Cast(type));557 break;558 }559 case sel_strlen: {560 TYPE_CHECK(String);561 data.Push((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 FormatterBytecode581 582} // namespace lldb_private583