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