blob: 4003f76d50984138a2c997230d7a09bbb6d64ac2 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/zxdb/console/format_value.h"
#include "garnet/bin/zxdb/common/test_with_loop.h"
#include "garnet/bin/zxdb/console/mock_format_value_process_context.h"
#include "garnet/bin/zxdb/console/output_buffer.h"
#include "garnet/bin/zxdb/expr/expr_value.h"
#include "garnet/bin/zxdb/symbols/array_type.h"
#include "garnet/bin/zxdb/symbols/base_type.h"
#include "garnet/bin/zxdb/symbols/collection.h"
#include "garnet/bin/zxdb/symbols/data_member.h"
#include "garnet/bin/zxdb/symbols/enumeration.h"
#include "garnet/bin/zxdb/symbols/function.h"
#include "garnet/bin/zxdb/symbols/function_type.h"
#include "garnet/bin/zxdb/symbols/inherited_from.h"
#include "garnet/bin/zxdb/symbols/member_ptr.h"
#include "garnet/bin/zxdb/symbols/mock_symbol_data_provider.h"
#include "garnet/bin/zxdb/symbols/modified_type.h"
#include "garnet/bin/zxdb/symbols/symbol_context.h"
#include "garnet/bin/zxdb/symbols/type_test_support.h"
#include "gtest/gtest.h"
namespace zxdb {
namespace {
fxl::RefPtr<BaseType> GetCharType() {
return fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsignedChar, 1,
"char");
}
fxl::RefPtr<BaseType> GetInt32Type() {
return fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 4, "int32_t");
}
fxl::RefPtr<ModifiedType> GetCharPointerType() {
return fxl::MakeRefCounted<ModifiedType>(Symbol::kTagPointerType,
LazySymbol(GetCharType()));
}
class FormatValueTest : public TestWithLoop {
public:
FormatValueTest()
: provider_(fxl::MakeRefCounted<MockSymbolDataProvider>()) {}
MockFormatValueProcessContext& process_context() { return process_context_; }
MockSymbolDataProvider* provider() { return provider_.get(); }
// Synchronously calls FormatExprValue, returning the result.
std::string SyncFormatValue(const ExprValue& value,
const FormatExprValueOptions& opts) {
bool called = false;
std::string output;
// Makes a heap-allocated copy of the ProcessContext for the formatter to
// manage.
auto formatter = fxl::MakeRefCounted<FormatValue>(
std::make_unique<MockFormatValueProcessContext>(process_context_));
formatter->AppendValue(provider_, value, opts);
formatter->Complete([&called, &output](OutputBuffer out) {
called = true;
output = out.AsString();
debug_ipc::MessageLoop::Current()->QuitNow();
});
if (called)
return output;
loop().Run();
EXPECT_TRUE(called);
return output;
}
private:
MockFormatValueProcessContext process_context_;
fxl::RefPtr<MockSymbolDataProvider> provider_;
};
} // namespace
TEST_F(FormatValueTest, Signed) {
FormatExprValueOptions opts;
// 8-bit.
ExprValue val_int8(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 1, "char"),
{123});
EXPECT_EQ("123", SyncFormatValue(val_int8, opts));
// 16-bit.
ExprValue val_int16(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 2, "short"),
{0xe0, 0xf0});
EXPECT_EQ("-3872", SyncFormatValue(val_int16, opts));
// 32-bit.
ExprValue val_int32(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 4, "int"),
{0x01, 0x02, 0x03, 0x04});
EXPECT_EQ("67305985", SyncFormatValue(val_int32, opts));
// 64-bit.
ExprValue val_int64(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 8, "long long"),
{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
EXPECT_EQ("-2", SyncFormatValue(val_int64, opts));
// Force a 32-bit float to an int.
ExprValue val_float(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeFloat, 4, "float"),
{0x04, 0x03, 0x02, 0x01});
opts.num_format = FormatExprValueOptions::NumFormat::kSigned;
EXPECT_EQ("16909060", SyncFormatValue(val_float, opts));
}
TEST_F(FormatValueTest, Unsigned) {
FormatExprValueOptions opts;
// 8-bit.
ExprValue val_int8(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsigned, 1, "char"),
{123});
EXPECT_EQ("123", SyncFormatValue(val_int8, opts));
// 16-bit.
ExprValue val_int16(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsigned, 1, "short"),
{0xe0, 0xf0});
EXPECT_EQ("61664", SyncFormatValue(val_int16, opts));
// 32-bit.
ExprValue val_int32(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsigned, 1, "int"),
{0x01, 0x02, 0x03, 0x04});
EXPECT_EQ("67305985", SyncFormatValue(val_int32, opts));
// 64-bit.
ExprValue val_int64(fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsigned,
1, "long long"),
{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
EXPECT_EQ("18446744073709551614", SyncFormatValue(val_int64, opts));
// Force a 32-bit float to an unsigned and a hex.
ExprValue val_float(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeFloat, 4, "float"),
{0x04, 0x03, 0x02, 0x01});
opts.num_format = FormatExprValueOptions::NumFormat::kUnsigned;
EXPECT_EQ("16909060", SyncFormatValue(val_float, opts));
opts.num_format = FormatExprValueOptions::NumFormat::kHex;
EXPECT_EQ("0x1020304", SyncFormatValue(val_float, opts));
}
TEST_F(FormatValueTest, Bool) {
FormatExprValueOptions opts;
// 8-bit true.
ExprValue val_true8(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeBoolean, 1, "bool"),
{0x01});
EXPECT_EQ("true", SyncFormatValue(val_true8, opts));
// 8-bit false.
ExprValue val_false8(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeBoolean, 1, "bool"),
{0x00});
EXPECT_EQ("false", SyncFormatValue(val_false8, opts));
// 32-bit true.
ExprValue val_false32(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeBoolean, 4, "bool"),
{0x00, 0x01, 0x00, 0x00});
EXPECT_EQ("false", SyncFormatValue(val_false8, opts));
}
TEST_F(FormatValueTest, Char) {
FormatExprValueOptions opts;
// 8-bit char.
ExprValue val_char8(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsignedChar, 1, "char"),
{'c'});
EXPECT_EQ("'c'", SyncFormatValue(val_char8, opts));
// Hex encoded 8-bit char.
ExprValue val_char8_zero(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsignedChar, 1, "char"),
{0});
EXPECT_EQ(R"('\x00')", SyncFormatValue(val_char8_zero, opts));
// Backslash-escaped 8-bit char.
ExprValue val_char8_quote(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsignedChar, 1, "char"),
{'\"'});
EXPECT_EQ(R"('\"')", SyncFormatValue(val_char8_quote, opts));
// 32-bit char (downcasted to 8 for printing).
ExprValue val_char32(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSignedChar, 4, "big"),
{'A', 1, 2, 3});
EXPECT_EQ("'A'", SyncFormatValue(val_char32, opts));
// 32-bit int forced to char.
opts.num_format = FormatExprValueOptions::NumFormat::kChar;
ExprValue val_int32(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 4, "int32_t"),
{'$', 0x01, 0x00, 0x00});
EXPECT_EQ("'$'", SyncFormatValue(val_int32, opts));
}
TEST_F(FormatValueTest, Float) {
FormatExprValueOptions opts;
uint8_t buffer[8];
// 32-bit float.
float in_float = 3.14159;
memcpy(buffer, &in_float, 4);
ExprValue val_float(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeFloat, 4, "float"),
std::vector<uint8_t>(&buffer[0], &buffer[4]));
EXPECT_EQ("3.14159", SyncFormatValue(val_float, opts));
// 64-bit float.
double in_double = 9.875e+12;
memcpy(buffer, &in_double, 8);
ExprValue val_double(
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeFloat, 8, "double"),
std::vector<uint8_t>(&buffer[0], &buffer[8]));
EXPECT_EQ("9.875e+12", SyncFormatValue(val_double, opts));
}
TEST_F(FormatValueTest, Pointer) {
FormatExprValueOptions opts;
auto base_type =
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 1, "int");
auto ptr_type = fxl::MakeRefCounted<ModifiedType>(Symbol::kTagPointerType,
LazySymbol(base_type));
std::vector<uint8_t> data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
ExprValue value(ptr_type, data);
// Print normally. Pointers always display their types.
EXPECT_EQ("(int*) 0x807060504030201", SyncFormatValue(value, opts));
// Print with type printing forced on. The result should be the same (the
// type shouldn't be duplicated).
opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
EXPECT_EQ("(int*) 0x807060504030201", SyncFormatValue(value, opts));
// Minimal formatting should omit the type name
opts.verbosity = FormatExprValueOptions::Verbosity::kMinimal;
EXPECT_EQ("(*) 0x807060504030201", SyncFormatValue(value, opts));
// Test an invalid one with an incorrect size.
data.resize(7);
opts.verbosity = FormatExprValueOptions::Verbosity::kMedium;
ExprValue bad_value(ptr_type, data);
EXPECT_EQ(
"(int*) <The value of type 'int*' is the incorrect size (expecting 8, "
"got 7). Please file a bug.>",
SyncFormatValue(bad_value, opts));
}
TEST_F(FormatValueTest, GoodStrings) {
FormatExprValueOptions opts;
constexpr uint64_t kAddress = 0x1100;
std::vector<uint8_t> data = {'A', 'B', 'C', 'D', 'E', 'F',
'\n', 0x01, 'z', '\\', '"', 0};
provider()->AddMemory(kAddress, data);
// Little-endian version of the address.
std::vector<uint8_t> address_data = {0x00, 0x11, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
// This string is a char* and it should stop printing at the null terminator.
const char kExpected[] = R"("ABCDEF\n\x01z\\\"")";
auto ptr_type = GetCharPointerType();
EXPECT_EQ(kExpected,
SyncFormatValue(ExprValue(ptr_type, address_data), opts));
// Force type info.
opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
EXPECT_EQ(std::string("(char*) ") + kExpected,
SyncFormatValue(ExprValue(ptr_type, address_data), opts));
// This string has the same data but is type encoded as char[12], it should
// give the same output (except for type info).
opts.verbosity = FormatExprValueOptions::Verbosity::kMedium;
auto array_type = fxl::MakeRefCounted<ArrayType>(GetCharType(), 12);
EXPECT_EQ(kExpected, SyncFormatValue(ExprValue(array_type, data), opts));
// Force type info.
opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
EXPECT_EQ(std::string("(char[12]) ") + kExpected,
SyncFormatValue(ExprValue(array_type, data), opts));
}
TEST_F(FormatValueTest, BadStrings) {
FormatExprValueOptions opts;
std::vector<uint8_t> address_data = {0x00, 0x11, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
// Should report invalid pointer.
auto ptr_type = GetCharPointerType();
ExprValue ptr_value(ptr_type, address_data);
EXPECT_EQ("0x1100 <invalid pointer>", SyncFormatValue(ptr_value, opts));
// A null string should print just the null and not say invalid.
ExprValue null_value(ptr_type, std::vector<uint8_t>(sizeof(uint64_t)));
EXPECT_EQ("0x0", SyncFormatValue(null_value, opts));
}
TEST_F(FormatValueTest, TruncatedString) {
FormatExprValueOptions opts;
constexpr uint64_t kAddress = 0x1100;
provider()->AddMemory(kAddress, {'A', 'B', 'C', 'D', 'E', 'F'});
// Little-endian version of kAddress.
std::vector<uint8_t> address_data = {0x00, 0x11, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
// This string doesn't end in a null terminator but rather invalid memory.
// We should print as much as we have.
auto ptr_type = GetCharPointerType();
EXPECT_EQ(R"("ABCDEF")",
SyncFormatValue(ExprValue(ptr_type, address_data), opts));
// Should only report the first 4 chars with a ... indicator.
opts.max_array_size = 4; // Truncate past this value.
EXPECT_EQ(R"("ABCD"...)",
SyncFormatValue(ExprValue(ptr_type, address_data), opts));
}
TEST_F(FormatValueTest, EmptyAndBadArray) {
FormatExprValueOptions opts;
// Array of two int32's: [1, 2]
constexpr uint64_t kAddress = 0x1100;
ExprValueSource source(kAddress);
// Empty array with valid pointer.
auto empty_array_type = fxl::MakeRefCounted<ArrayType>(GetInt32Type(), 0);
EXPECT_EQ(R"({})", SyncFormatValue(ExprValue(empty_array_type,
std::vector<uint8_t>(), source),
opts));
// Array type declares a size but there's no data.
auto array_type = fxl::MakeRefCounted<ArrayType>(GetInt32Type(), 1);
EXPECT_EQ(
R"(<Array data (0 bytes) is too small for the expected size (4 bytes).>)",
SyncFormatValue(ExprValue(array_type, std::vector<uint8_t>(), source),
opts));
}
TEST_F(FormatValueTest, TruncatedArray) {
FormatExprValueOptions opts;
opts.max_array_size = 2;
// Array of two int32's: {1, 2}
constexpr uint64_t kAddress = 0x1100;
ExprValueSource source(kAddress);
std::vector<uint8_t> data = {0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
auto array_type = fxl::MakeRefCounted<ArrayType>(GetInt32Type(), 2);
// This array has exactly the max size, we shouldn't mark it as truncated.
EXPECT_EQ("{1, 2}",
SyncFormatValue(ExprValue(array_type, data, source), opts));
// Try one with type info forced on. Only the root array type should have the
// type, not each individual element.
opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
EXPECT_EQ("(int32_t[2]) {1, 2}",
SyncFormatValue(ExprValue(array_type, data, source), opts));
// This one is truncated.
opts.max_array_size = 1;
EXPECT_EQ("(int32_t[2]) {1, ...}",
SyncFormatValue(ExprValue(array_type, data, source), opts));
}
TEST_F(FormatValueTest, Reference) {
FormatExprValueOptions opts;
auto base_type =
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 1, "int");
auto ref_type = fxl::MakeRefCounted<ModifiedType>(Symbol::kTagReferenceType,
LazySymbol(base_type));
constexpr uint64_t kAddress = 0x1100;
provider()->AddMemory(kAddress, {123, 0, 0, 0, 0, 0, 0, 0});
// This data refers to the address above.
std::vector<uint8_t> data = {0x00, 0x11, 0, 0, 0, 0, 0, 0};
ExprValue value(ref_type, data);
EXPECT_EQ("(int&) 0x1100 = 123", SyncFormatValue(value, opts));
// Forcing type info on shouldn't duplicate the type.
opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
EXPECT_EQ("(int&) 0x1100 = 123", SyncFormatValue(value, opts));
// Force with minimal formatting (no addr ot type info).
opts.verbosity = FormatExprValueOptions::Verbosity::kMinimal;
EXPECT_EQ("123", SyncFormatValue(value, opts));
// Test an invalid one with an invalid address.
std::vector<uint8_t> bad_data = {0x00, 0x22, 0, 0, 0, 0, 0, 0};
opts.verbosity = FormatExprValueOptions::Verbosity::kMedium;
value = ExprValue(ref_type, bad_data);
EXPECT_EQ("(int&) 0x2200 = <Invalid pointer 0x2200>",
SyncFormatValue(value, opts));
// Test an rvalue reference. This is treated the same as a regular reference
// from an interpretation and printing perspective.
auto rvalue_ref_type = fxl::MakeRefCounted<ModifiedType>(
Symbol::kTagRvalueReferenceType, LazySymbol(base_type));
value = ExprValue(rvalue_ref_type, data);
opts.verbosity = FormatExprValueOptions::Verbosity::kMedium;
EXPECT_EQ("(int&&) 0x1100 = 123", SyncFormatValue(value, opts));
opts.verbosity = FormatExprValueOptions::Verbosity::kMinimal;
EXPECT_EQ("123", SyncFormatValue(value, opts));
}
TEST_F(FormatValueTest, Structs) {
FormatExprValueOptions opts;
opts.num_format = FormatExprValueOptions::NumFormat::kHex;
auto int32_type = MakeInt32Type();
// Make an int reference. Reference type printing combined with struct type
// printing can get complicated.
auto int_ref = fxl::MakeRefCounted<ModifiedType>(Symbol::kTagReferenceType,
LazySymbol(int32_type));
// The references point to this data.
constexpr uint64_t kAddress = 0x1100;
provider()->AddMemory(kAddress, {0x12, 0, 0, 0});
// Struct with two values, an int and a int&, and a pair of two of those
// structs.
auto foo = MakeCollectionType(Symbol::kTagStructureType, "Foo",
{{"a", int32_type}, {"b", int_ref}});
auto pair = MakeCollectionType(Symbol::kTagStructureType, "Pair",
{{"first", foo}, {"second", foo}});
ExprValue pair_value(
pair, {0x11, 0x00, 0x11, 0x00, // (int32) a
0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (int32&) b
0x33, 0x00, 0x33, 0x00, // (int32) a
0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); // (int32&) b
// The references when not printing all types are printed after the
// struct member name.
EXPECT_EQ(
"{first = {a = 0x110011, b = (int32_t&) 0x1100 = 0x12}, "
"second = {a = 0x330033, b = (int32_t&) 0x1100 = 0x12}}",
SyncFormatValue(pair_value, opts));
// Force type info. Now the reference types move before the member names like
// the other types.
opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
EXPECT_EQ(
"(Pair) {(Foo) first = {(int32_t) a = 0x110011, (int32_t&) b = 0x1100 = "
"0x12}, "
"(Foo) second = {(int32_t) a = 0x330033, (int32_t&) b = 0x1100 = 0x12}}",
SyncFormatValue(pair_value, opts));
// Test an anonymous struct. Clang will generate structs with no names for
// things like closures.
auto anon_struct = fxl::MakeRefCounted<Collection>(Symbol::kTagStructureType);
auto anon_struct_ptr = fxl::MakeRefCounted<ModifiedType>(
Symbol::kTagPointerType, LazySymbol(anon_struct));
ExprValue anon_value(anon_struct_ptr,
{0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
EXPECT_EQ("((anon struct)*) 0x1100", SyncFormatValue(anon_value, opts));
}
// GDB and LLDB both print all members of a union and accept the possibility
// that sometimes one of them might be garbage, we do the same.
TEST_F(FormatValueTest, Union) {
FormatExprValueOptions opts;
// Define a union type with two int32 values.
auto int32_type = MakeInt32Type();
auto union_type = fxl::MakeRefCounted<Collection>(Symbol::kTagUnionType);
union_type->set_byte_size(int32_type->byte_size());
union_type->set_assigned_name("MyUnion");
std::vector<LazySymbol> data_members;
auto member_1 = fxl::MakeRefCounted<DataMember>();
member_1->set_assigned_name("a");
member_1->set_type(LazySymbol(int32_type));
member_1->set_member_location(0);
data_members.push_back(LazySymbol(member_1));
auto member_2 = fxl::MakeRefCounted<DataMember>();
member_2->set_assigned_name("b");
member_2->set_type(LazySymbol(int32_type));
member_2->set_member_location(0);
data_members.push_back(LazySymbol(member_2));
union_type->set_data_members(std::move(data_members));
ExprValue value(union_type, {42, 0, 0, 0});
EXPECT_EQ("{a = 42, b = 42}", SyncFormatValue(value, opts));
}
// Tests formatting when a class has derived base classes.
TEST_F(FormatValueTest, DerivedClasses) {
auto int32_type = MakeInt32Type();
auto base = MakeCollectionType(Symbol::kTagStructureType, "Base",
{{"a", int32_type}, {"b", int32_type}});
// This second base class is empty, it should be omitted from the output.
auto empty_base = fxl::MakeRefCounted<Collection>(Symbol::kTagClassType);
empty_base->set_assigned_name("EmptyBase");
// Derived class, leave enough room to hold |Base|.
auto derived = MakeCollectionTypeWithOffset(
Symbol::kTagStructureType, "Derived", base->byte_size(),
{{"c", int32_type}, {"d", int32_type}});
auto inherited = fxl::MakeRefCounted<InheritedFrom>(LazySymbol(base), 0);
auto empty_inherited =
fxl::MakeRefCounted<InheritedFrom>(LazySymbol(empty_base), 0);
derived->set_inherited_from(
{LazySymbol(inherited), LazySymbol(empty_inherited)});
uint8_t kAValue = 1;
uint8_t kBValue = 2;
uint8_t kCValue = 3;
uint8_t kDValue = 4;
ExprValue value(derived, {kAValue, 0, 0, 0, // (int32) Base.a
kBValue, 0, 0, 0, // (int32) Base.b
kCValue, 0, 0, 0, // (int32) Derived.c
kDValue, 0, 0, 0}); // (int32) Derived.d
// Default formatting. Only the Base should be printed, EmptyBase should be
// omitted because it has no data.
FormatExprValueOptions opts;
EXPECT_EQ("{Base = {a = 1, b = 2}, c = 3, d = 4}",
SyncFormatValue(value, opts));
// Force types on. The type of the base class should not be duplicated.
opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
EXPECT_EQ(
"(Derived) {Base = {(int32_t) a = 1, (int32_t) b = 2}, (int32_t) c = 3, "
"(int32_t) d = 4}",
SyncFormatValue(value, opts));
}
TEST_F(FormatValueTest, Enumeration) {
// Unsigned 64-bit enum.
Enumeration::Map unsigned_map;
unsigned_map[0] = "kZero";
unsigned_map[1] = "kOne";
unsigned_map[std::numeric_limits<uint64_t>::max()] = "kMax";
auto unsigned_enum = fxl::MakeRefCounted<Enumeration>(
"UnsignedEnum", LazySymbol(), 8, false, unsigned_map);
// Found value
FormatExprValueOptions opts;
EXPECT_EQ("kZero",
SyncFormatValue(ExprValue(unsigned_enum, {0, 0, 0, 0, 0, 0, 0, 0}),
opts));
EXPECT_EQ("kMax",
SyncFormatValue(ExprValue(unsigned_enum, {0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff}),
opts));
// Found value forced to hex.
FormatExprValueOptions hex_opts;
hex_opts.num_format = FormatExprValueOptions::NumFormat::kHex;
EXPECT_EQ("0xffffffffffffffff",
SyncFormatValue(ExprValue(unsigned_enum, {0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff}),
hex_opts));
// Not found value.
EXPECT_EQ("12",
SyncFormatValue(ExprValue(unsigned_enum, {12, 0, 0, 0, 0, 0, 0, 0}),
opts));
// Signed 32-bit enum.
Enumeration::Map signed_map;
signed_map[0] = "kZero";
signed_map[static_cast<uint64_t>(-5)] = "kMinusFive";
signed_map[static_cast<uint64_t>(std::numeric_limits<int32_t>::max())] =
"kMax";
auto signed_enum = fxl::MakeRefCounted<Enumeration>(
"SignedEnum", LazySymbol(), 4, true, signed_map);
// Found values.
EXPECT_EQ("kZero",
SyncFormatValue(ExprValue(signed_enum, {0, 0, 0, 0}), opts));
EXPECT_EQ(
"kMinusFive",
SyncFormatValue(ExprValue(signed_enum, {0xfb, 0xff, 0xff, 0xff}), opts));
// Not-found value.
EXPECT_EQ("-4", SyncFormatValue(
ExprValue(signed_enum, {0xfc, 0xff, 0xff, 0xff}), opts));
// Not-found signed value printed as hex should be unsigned.
EXPECT_EQ("0xffffffff",
SyncFormatValue(ExprValue(signed_enum, {0xff, 0xff, 0xff, 0xff}),
hex_opts));
// Force type info.
FormatExprValueOptions type_opts;
type_opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
EXPECT_EQ("(SignedEnum) kZero",
SyncFormatValue(ExprValue(signed_enum, {0, 0, 0, 0}), type_opts));
EXPECT_EQ("(SignedEnum) -4",
SyncFormatValue(ExprValue(signed_enum, {0xfc, 0xff, 0xff, 0xff}),
type_opts));
}
TEST_F(FormatValueTest, FunctionPtr) {
// This is a function type. There isn't a corresponding C/C++ type for a
// function type (without a pointer modifier) but we define it anyway in
// case it comes up (possibly another language).
auto func_type = fxl::MakeRefCounted<FunctionType>(LazySymbol(),
std::vector<LazySymbol>());
// This type is "void (*)()"
auto func_ptr_type = fxl::MakeRefCounted<ModifiedType>(
Symbol::kTagPointerType, LazySymbol(func_type));
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
auto function = fxl::MakeRefCounted<Function>(Symbol::kTagSubprogram);
function->set_assigned_name("MyFunc");
// Map the address to point to the function.
constexpr uint64_t kAddress = 0x1234;
process_context().AddResult(
kAddress, Location(kAddress, FileLine("file.cc", 21), 0, symbol_context,
LazySymbol(function)));
// Function.
FormatExprValueOptions type_opts;
type_opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
ExprValue null_func(func_type, {0, 0, 0, 0, 0, 0, 0, 0});
EXPECT_EQ("(void()) 0x0", SyncFormatValue(null_func, type_opts));
// Null function pointer.
FormatExprValueOptions opts;
ExprValue null_ptr(func_ptr_type, {0, 0, 0, 0, 0, 0, 0, 0});
EXPECT_EQ("0x0", SyncFormatValue(null_ptr, opts));
// Null function pointer with forced type info,
EXPECT_EQ("(void (*)()) 0x0", SyncFormatValue(null_ptr, type_opts));
// Function pointer to unknown memory is printed in hex by default.
EXPECT_EQ("0x5",
SyncFormatValue(ExprValue(func_ptr_type, {5, 0, 0, 0, 0, 0, 0, 0}),
opts));
// Found symbol (matching kAddress) should be printed.
ExprValue good_ptr(func_ptr_type, {0x34, 0x12, 0, 0, 0, 0, 0, 0});
EXPECT_EQ("&MyFunc", SyncFormatValue(good_ptr, opts));
// Force output as hex even when the function is matched.
FormatExprValueOptions hex_opts;
hex_opts.num_format = FormatExprValueOptions::NumFormat::kHex;
EXPECT_EQ("0x1234", SyncFormatValue(good_ptr, hex_opts));
// Member function pointer. The type naming of function pointers is tested by
// the MemberPtr class, and otherwise the code paths are the same, so here
// we only need to verify things are hooked up.
auto containing = fxl::MakeRefCounted<Collection>(Symbol::kTagClassType);
containing->set_assigned_name("MyClass");
auto member_func = fxl::MakeRefCounted<MemberPtr>(LazySymbol(containing),
LazySymbol(func_type));
ExprValue null_member_func_ptr(member_func, {0, 0, 0, 0, 0, 0, 0, 0});
EXPECT_EQ("0x0", SyncFormatValue(null_member_func_ptr, opts));
EXPECT_EQ("(void (MyClass::*)()) 0x0",
SyncFormatValue(null_member_func_ptr, type_opts));
// Member function to a known symbol. This doesn't resolve to something that
// looks like a class member, but that's OK, wherever the address points to
// is what we print.
ExprValue good_member_func_ptr(member_func, {0x34, 0x12, 0, 0, 0, 0, 0, 0});
EXPECT_EQ("(void (MyClass::*)()) &MyFunc",
SyncFormatValue(good_member_func_ptr, type_opts));
}
// This tests pointers to member data. Pointers to member functions were tested
// by the FunctionPtr test.
TEST_F(FormatValueTest, MemberPtr) {
auto containing = fxl::MakeRefCounted<Collection>(Symbol::kTagClassType);
containing->set_assigned_name("MyClass");
auto int32_type = GetInt32Type();
auto member_int32 = fxl::MakeRefCounted<MemberPtr>(LazySymbol(containing),
LazySymbol(int32_type));
// Null pointer.
FormatExprValueOptions opts;
ExprValue null_member_ptr(member_int32, {0, 0, 0, 0, 0, 0, 0, 0});
EXPECT_EQ("(int32_t MyClass::*) 0x0", SyncFormatValue(null_member_ptr, opts));
// Regular pointer with types forced on.
FormatExprValueOptions type_opts;
type_opts.verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
ExprValue good_member_ptr(member_int32, {0x34, 0x12, 0, 0, 0, 0, 0, 0});
EXPECT_EQ("(int32_t MyClass::*) 0x1234",
SyncFormatValue(good_member_ptr, type_opts));
}
} // namespace zxdb