blob: 042eb24467976a69d472c46449310b6a7c54389f [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/expr/resolve_collection.h"
#include "garnet/bin/zxdb/common/err.h"
#include "garnet/bin/zxdb/expr/expr_value.h"
#include "garnet/bin/zxdb/expr/identifier.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/inherited_from.h"
#include "garnet/bin/zxdb/symbols/modified_type.h"
#include "garnet/bin/zxdb/symbols/type_test_support.h"
#include "gtest/gtest.h"
namespace zxdb {
namespace {
// Defines a class with two member types "a" and "b". It puts the definitions
// of "a" and "b' members into the two out params.
fxl::RefPtr<Collection> GetTestClassType(const DataMember** member_a,
const DataMember** member_b) {
auto int32_type = MakeInt32Type();
auto sc = MakeCollectionType(Symbol::kTagStructureType, "Foo",
{{"a", int32_type}, {"b", int32_type}});
*member_a = sc->data_members()[0].Get()->AsDataMember();
*member_b = sc->data_members()[1].Get()->AsDataMember();
return sc;
}
// Helper function that calls ResolveMember with an identifier with the
// containing value.
Err ResolveMemberFromString(const ExprValue& base, const std::string& name,
ExprValue* out) {
auto [err, ident] = Identifier::FromString(name);
if (err.has_error())
return err;
return ResolveMember(base, ident, out);
}
} // namespace
TEST(ResolveCollection, GoodMemberAccess) {
const DataMember* a_data;
const DataMember* b_data;
auto sc = GetTestClassType(&a_data, &b_data);
// Make this const volatile to add extra layers.
auto vol_sc = fxl::MakeRefCounted<ModifiedType>(Symbol::kTagVolatileType,
LazySymbol(sc));
auto const_vol_sc = fxl::MakeRefCounted<ModifiedType>(Symbol::kTagConstType,
LazySymbol(vol_sc));
// This struct has the values 1 and 2 in it.
constexpr uint64_t kBaseAddr = 0x11000;
ExprValue base(const_vol_sc, {0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00},
ExprValueSource(kBaseAddr));
// Resolve A.
ExprValue out;
Err err = ResolveMember(base, a_data, &out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ("int32_t", out.type()->GetAssignedName());
EXPECT_EQ(4u, out.data().size());
EXPECT_EQ(1, out.GetAs<int32_t>());
EXPECT_EQ(kBaseAddr, out.source().address());
// Resolve A by name.
ExprValue out_by_name;
err = ResolveMemberFromString(base, "a", &out_by_name);
EXPECT_EQ(out, out_by_name);
// Resolve B.
out = ExprValue();
err = ResolveMember(base, b_data, &out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ("int32_t", out.type()->GetAssignedName());
EXPECT_EQ(4u, out.data().size());
EXPECT_EQ(2, out.GetAs<int32_t>());
EXPECT_EQ(kBaseAddr + 4, out.source().address());
// Resolve B by name.
out_by_name = ExprValue();
err = ResolveMemberFromString(base, "b", &out_by_name);
EXPECT_EQ(out, out_by_name);
}
TEST(ResolveCollection, BadMemberArgs) {
const DataMember* a_data;
const DataMember* b_data;
auto sc = GetTestClassType(&a_data, &b_data);
// Test null base class pointer.
ExprValue out;
Err err = ResolveMember(ExprValue(), a_data, &out);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("Can't resolve data member on non-struct/class value.", err.msg());
constexpr uint64_t kBaseAddr = 0x11000;
ExprValue base(sc, {0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00},
ExprValueSource(kBaseAddr));
// Null datat member pointer.
out = ExprValue();
err = ResolveMember(base, nullptr, &out);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("Invalid data member for struct 'Foo'.", err.msg());
}
TEST(ResolveCollection, BadMemberAccess) {
const DataMember* a_data;
const DataMember* b_data;
auto sc = GetTestClassType(&a_data, &b_data);
constexpr uint64_t kBaseAddr = 0x11000;
ExprValue base(sc, {0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00},
ExprValueSource(kBaseAddr));
// Lookup by name that doesn't exist.
ExprValue out;
Err err = ResolveMemberFromString(base, "c", &out);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("No member 'c' in struct 'Foo'.", err.msg());
// Lookup by a DataMember that references outside of the struct (in this
// case, by one byte).
auto bad_member = fxl::MakeRefCounted<DataMember>();
bad_member->set_assigned_name("c");
bad_member->set_type(LazySymbol(MakeInt32Type()));
bad_member->set_member_location(5);
out = ExprValue();
err = ResolveMember(base, bad_member.get(), &out);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("Invalid data member for struct 'Foo'.", err.msg());
}
// Tests foo.bar where bar is in a derived class of foo's type.
TEST(ResolveCollection, DerivedClass) {
const DataMember* a_data;
const DataMember* b_data;
auto base = GetTestClassType(&a_data, &b_data);
auto derived = fxl::MakeRefCounted<Collection>(Symbol::kTagClassType);
uint32_t base_offset = 4; // Offset in derived of base.
auto inherited =
fxl::MakeRefCounted<InheritedFrom>(LazySymbol(base), base_offset);
derived->set_inherited_from({LazySymbol(inherited)});
// This struct has the values 1 and 2 in it, offset by 4 bytes (the offset
// within "derived" of "base").
constexpr uint64_t kBaseAddr = 0x11000;
ExprValue value(derived, {0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00},
ExprValueSource(kBaseAddr));
// Resolve B by name.
ExprValue out;
Err err = ResolveMemberFromString(value, "b", &out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ("int32_t", out.type()->GetAssignedName());
EXPECT_EQ(4u, out.data().size());
EXPECT_EQ(2, out.GetAs<int32_t>());
// Offset of B in "derived".
EXPECT_EQ(kBaseAddr + base_offset + 4, out.source().address());
// Test extracting the base class from the derived one.
ExprValue base_value;
err = ResolveInherited(value, inherited.get(), &base_value);
EXPECT_FALSE(err.has_error());
EXPECT_EQ(ExprValue(base, {0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00},
ExprValueSource(kBaseAddr + base_offset)),
base_value);
}
} // namespace zxdb