Fix the internal type of enum values

EnumFieldIR has member functions that determine whether the enum value
is signed. The JSON dumper uses the functions to distinguish between
negative values and values >= (1 << 63).

Bug: 277299761
Test: ./test.py
Change-Id: If5f3a33aaf5f53f39019fdd10dde366d48a50340
This commit is contained in:
Hsin-Yi Chen
2023-04-11 18:36:15 +08:00
parent 116fa3048f
commit 62081d11b4
12 changed files with 259 additions and 17 deletions

View File

@@ -882,8 +882,12 @@ bool EnumDeclWrapper::SetupEnumFields(repr::EnumTypeIR *enump) {
clang::EnumDecl::enumerator_iterator enum_it = enum_decl_->enumerator_begin();
while (enum_it != enum_decl_->enumerator_end()) {
std::string name = enum_it->getQualifiedNameAsString();
uint64_t field_value = enum_it->getInitVal().getExtValue();
enump->AddEnumField(repr::EnumFieldIR(name, field_value));
const llvm::APSInt &value = enum_it->getInitVal();
if (value.isUnsigned()) {
enump->AddEnumField(repr::EnumFieldIR(name, value.getZExtValue()));
} else {
enump->AddEnumField(repr::EnumFieldIR(name, value.getSExtValue()));
}
enum_it++;
}
return true;

View File

@@ -147,9 +147,10 @@ void AbiDiffHelper::CompareEnumFields(
utils::FindCommonElements(old_fields_map, new_fields_map);
std::vector<EnumFieldDiffIR> enum_field_diffs;
for (auto &&common_fields : cf) {
if (common_fields.first->GetValue() != common_fields.second->GetValue()) {
if (common_fields.first->GetSignedValue() !=
common_fields.second->GetSignedValue()) {
EnumFieldDiffIR enum_field_diff_ir(common_fields.first,
common_fields.second);
common_fields.second);
enum_field_diffs.emplace_back(std::move(enum_field_diff_ir));
}
}

View File

@@ -447,20 +447,29 @@ class RecordTypeIR : public TypeIR, public TemplatedArtifactIR {
class EnumFieldIR {
public:
EnumFieldIR(const std::string &name, int value)
: name_(name), value_(value) {}
EnumFieldIR(const std::string &name, int64_t value)
: name_(name), signed_value_(value), is_signed_(true) {}
EnumFieldIR(const std::string &name, uint64_t value)
: name_(name), unsigned_value_(value), is_signed_(false) {}
const std::string &GetName() const {
return name_;
}
int GetValue() const {
return value_;
}
bool IsSigned() const { return is_signed_; }
int64_t GetSignedValue() const { return signed_value_; }
uint64_t GetUnsignedValue() const { return unsigned_value_; }
protected:
std::string name_;
int value_ = 0;
union {
int64_t signed_value_;
uint64_t unsigned_value_;
};
bool is_signed_;
};
class EnumTypeIR : public TypeIR {

View File

@@ -203,7 +203,12 @@ static JsonObject ConvertEnumFieldIR(const EnumFieldIR *enum_field_ir) {
JsonObject enum_field;
enum_field.Set("name", enum_field_ir->GetName());
// Never omit enum values.
enum_field["enum_field_value"] = Json::Int64(enum_field_ir->GetValue());
Json::Value &enum_field_value = enum_field["enum_field_value"];
if (enum_field_ir->IsSigned()) {
enum_field_value = Json::Int64(enum_field_ir->GetSignedValue());
} else {
enum_field_value = Json::UInt64(enum_field_ir->GetUnsignedValue());
}
return enum_field;
}

View File

@@ -76,11 +76,16 @@ bool JsonObjectRef::GetBool(const std::string &key) const {
}
int64_t JsonObjectRef::GetInt(const std::string &key) const {
return Get(key, json_0, &Json::Value::isIntegral).asInt64();
return Get(key, json_0, &Json::Value::isInt64).asInt64();
}
uint64_t JsonObjectRef::GetUint(const std::string &key) const {
return Get(key, json_0, &Json::Value::isIntegral).asUInt64();
return Get(key, json_0, &Json::Value::isUInt64).asUInt64();
}
const Json::Value &JsonObjectRef::GetIntegralValue(
const std::string &key) const {
return Get(key, json_0, &Json::Value::isIntegral);
}
std::string JsonObjectRef::GetString(const std::string &key) const {
@@ -248,9 +253,13 @@ void JsonIRReader::ReadVTableLayout(const JsonObjectRef &record_type,
void JsonIRReader::ReadEnumFields(const JsonObjectRef &enum_type,
EnumTypeIR *enum_ir) {
for (auto &&field : enum_type.GetObjects("enum_fields")) {
EnumFieldIR enum_field_ir(field.GetString("name"),
field.GetInt("enum_field_value"));
enum_ir->AddEnumField(std::move(enum_field_ir));
std::string name = field.GetString("name");
const Json::Value &value = field.GetIntegralValue("enum_field_value");
if (value.isUInt64()) {
enum_ir->AddEnumField(EnumFieldIR(name, value.asUInt64()));
} else {
enum_ir->AddEnumField(EnumFieldIR(name, value.asInt64()));
}
}
}

View File

@@ -46,6 +46,9 @@ class JsonObjectRef {
// Default to 0.
uint64_t GetUint(const std::string &key) const;
// Default to 0.
const Json::Value &GetIntegralValue(const std::string &key) const;
// Default to "".
std::string GetString(const std::string &key) const;

View File

@@ -356,7 +356,11 @@ inline bool SetIRToProtobufEnumField(
return true;
}
enum_field_protobuf->set_name(enum_field_ir->GetName());
enum_field_protobuf->set_enum_field_value(enum_field_ir->GetValue());
// The "enum_field_value" in the .proto is a signed 64-bit integer. An
// unsigned integer >= (1 << 63) is represented with a negative integer in the
// dump file. Despite the wrong representation, the diff result isn't affected
// because every integer has a unique representation.
enum_field_protobuf->set_enum_field_value(enum_field_ir->GetSignedValue());
return true;
}

View File

@@ -0,0 +1,23 @@
#include <cstdint>
enum Int8 : int8_t {
ZERO = 0,
MINUS_1 = (int8_t)-1,
};
enum Uint8 : uint8_t {
UNSIGNED_255 = UINT8_MAX,
};
enum Int64 : int64_t {
SIGNED_MAX = INT64_MAX,
SIGNED_MIN = INT64_MIN,
};
enum Uint64 : uint64_t {
UNSIGNED_MAX = UINT64_MAX,
};
extern "C" {
void function(Int8, Uint8, Int64, Uint64);
}

View File

@@ -0,0 +1,4 @@
libenum {
global:
function;
};

View File

@@ -789,6 +789,16 @@ TEST_MODULES = [
export_include_dirs=['integration/union/include'],
linker_flags=['-output-format', 'Json'],
),
LsdumpModule(
name='libenum',
arch='arm64',
srcs=['integration/enum/include/base.h'],
version_script='integration/enum/map.txt',
export_include_dirs=['integration/enum/include'],
dumper_flags=['-output-format', 'Json'],
linker_flags=['-input-format', 'Json', '-output-format', 'Json'],
has_reference_dump=True,
),
]
TEST_MODULES = {m.name: m for m in TEST_MODULES}

View File

@@ -0,0 +1,167 @@
{
"array_types" : [],
"builtin_types" :
[
{
"alignment" : 1,
"is_integral" : true,
"linker_set_key" : "_ZTIa",
"name" : "signed char",
"referenced_type" : "_ZTIa",
"self_type" : "_ZTIa",
"size" : 1
},
{
"alignment" : 1,
"is_integral" : true,
"is_unsigned" : true,
"linker_set_key" : "_ZTIh",
"name" : "unsigned char",
"referenced_type" : "_ZTIh",
"self_type" : "_ZTIh",
"size" : 1
},
{
"alignment" : 8,
"is_integral" : true,
"linker_set_key" : "_ZTIl",
"name" : "long",
"referenced_type" : "_ZTIl",
"self_type" : "_ZTIl",
"size" : 8
},
{
"alignment" : 8,
"is_integral" : true,
"is_unsigned" : true,
"linker_set_key" : "_ZTIm",
"name" : "unsigned long",
"referenced_type" : "_ZTIm",
"self_type" : "_ZTIm",
"size" : 8
},
{
"linker_set_key" : "_ZTIv",
"name" : "void",
"referenced_type" : "_ZTIv",
"self_type" : "_ZTIv"
}
],
"elf_functions" :
[
{
"name" : "function"
}
],
"elf_objects" : [],
"enum_types" :
[
{
"alignment" : 1,
"enum_fields" :
[
{
"enum_field_value" : 0,
"name" : "ZERO"
},
{
"enum_field_value" : -1,
"name" : "MINUS_1"
}
],
"linker_set_key" : "_ZTI4Int8",
"name" : "Int8",
"referenced_type" : "_ZTI4Int8",
"self_type" : "_ZTI4Int8",
"size" : 1,
"source_file" : "development/vndk/tools/header-checker/tests/integration/enum/include/base.h",
"underlying_type" : "_ZTIa"
},
{
"alignment" : 8,
"enum_fields" :
[
{
"enum_field_value" : 9223372036854775807,
"name" : "SIGNED_MAX"
},
{
"enum_field_value" : -9223372036854775808,
"name" : "SIGNED_MIN"
}
],
"linker_set_key" : "_ZTI5Int64",
"name" : "Int64",
"referenced_type" : "_ZTI5Int64",
"self_type" : "_ZTI5Int64",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/enum/include/base.h",
"underlying_type" : "_ZTIl"
},
{
"alignment" : 1,
"enum_fields" :
[
{
"enum_field_value" : 255,
"name" : "UNSIGNED_255"
}
],
"linker_set_key" : "_ZTI5Uint8",
"name" : "Uint8",
"referenced_type" : "_ZTI5Uint8",
"self_type" : "_ZTI5Uint8",
"size" : 1,
"source_file" : "development/vndk/tools/header-checker/tests/integration/enum/include/base.h",
"underlying_type" : "_ZTIh"
},
{
"alignment" : 8,
"enum_fields" :
[
{
"enum_field_value" : 18446744073709551615,
"name" : "UNSIGNED_MAX"
}
],
"linker_set_key" : "_ZTI6Uint64",
"name" : "Uint64",
"referenced_type" : "_ZTI6Uint64",
"self_type" : "_ZTI6Uint64",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/enum/include/base.h",
"underlying_type" : "_ZTIm"
}
],
"function_types" : [],
"functions" :
[
{
"function_name" : "function",
"linker_set_key" : "function",
"parameters" :
[
{
"referenced_type" : "_ZTI4Int8"
},
{
"referenced_type" : "_ZTI5Uint8"
},
{
"referenced_type" : "_ZTI5Int64"
},
{
"referenced_type" : "_ZTI6Uint64"
}
],
"return_type" : "_ZTIv",
"source_file" : "development/vndk/tools/header-checker/tests/integration/enum/include/base.h"
}
],
"global_vars" : [],
"lvalue_reference_types" : [],
"pointer_types" : [],
"qualified_types" : [],
"record_types" : [],
"rvalue_reference_types" : []
}

View File

@@ -485,6 +485,9 @@ class HeaderCheckerTest(unittest.TestCase):
self.assertNotIn("fields_added", diff)
self.assertNotIn("fields_removed", diff)
def test_enum_diff(self):
self.prepare_and_absolute_diff_all_archs("libenum", "libenum")
if __name__ == '__main__':
unittest.main()