C23 lets an enum fix its underlying type, e.g. "enum E : long long { ... }".
The C grammar already exposes it as the "underlying_type" field of an
enum_specifier, and RzBaseType already has a "type" slot documented as
used by enums, but the parser ignored the field and always left it NULL.
- parse_enum_node() now reads the "underlying_type" field and stores the
parsed type on RzBaseType::type (reusing parse_type_node_single(), so
primitive, sized and typedef'd integer types are all handled). Classic
enums keep a NULL underlying type.
- The pretty printer emits " : <type>" between the enum name and its body
when an underlying type is present, so "tc"/"tcd"/"tec" round-trip it.
- enum_bitsize() now derives the width from the underlying type instead of
the hardcoded 32-bit default (resolving the long-standing FIXME); it
still falls back to 32 for a classic enum.
Single-token underlying types (int, uint64_t, char, ...) work end-to-end
with the bundled grammar revision: the existing "enhanced enum" db test is
updated to round-trip "enum v : int" and a unit test covers
"enum EU : uint64_t". Multi-word underlying types (long long, unsigned int)
are added as BROKEN db tests; the bundled grammar parses them to an ERROR
node and drops the underlying type, so these tests fail for now and will
pass once rizin-grammar-c accepts sized type specifiers as the enum
underlying type. No further rizin change is needed for that step.
Co-authored-by: Anton Kochkov <anton.kochkov@gmail.com>
This commit is contained in:
parent
308d53c9f7
commit
e23ac87bc9
12 changed files with 347 additions and 33 deletions
|
|
@ -754,6 +754,21 @@ RZ_API bool rz_project_migrate_v21_v22(RzProject *prj, RzSerializeResultInfo *re
|
|||
return true;
|
||||
}
|
||||
|
||||
// --
|
||||
// Migration 22 -> 23
|
||||
//
|
||||
// Changes:
|
||||
// Enums can now declare a fixed underlying type ("enum E : long long { ... }"),
|
||||
// serialized under "enum.<name>.@type" in "/core/analysis/types". Older
|
||||
// projects simply lack that key and the deserializer treats its absence as a
|
||||
// classic (int-backed) enum, so there is nothing to migrate.
|
||||
|
||||
RZ_API bool rz_project_migrate_v22_v23(RzProject *prj, RzSerializeResultInfo *res) {
|
||||
// there is nothing to be done since the deserializer treats a missing
|
||||
// "enum.<name>.@type" as a classic enum without a fixed underlying type
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) = {
|
||||
rz_project_migrate_v1_v2,
|
||||
rz_project_migrate_v2_v3,
|
||||
|
|
@ -776,6 +791,7 @@ static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) =
|
|||
rz_project_migrate_v19_v20,
|
||||
rz_project_migrate_v20_v21,
|
||||
rz_project_migrate_v21_v22,
|
||||
rz_project_migrate_v22_v23,
|
||||
};
|
||||
|
||||
/// Migrate the given project to the current version in-place
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RZ_PROJECT_VERSION 22
|
||||
#define RZ_PROJECT_VERSION 23
|
||||
|
||||
typedef Sdb RzProject;
|
||||
|
||||
|
|
@ -67,6 +67,7 @@ RZ_API bool rz_project_migrate_v18_v19(RzProject *prj, RzSerializeResultInfo *re
|
|||
RZ_API bool rz_project_migrate_v19_v20(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate_v20_v21(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate_v21_v22(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate_v22_v23(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate(RzProject *prj, unsigned long version, RzSerializeResultInfo *res);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -1090,6 +1090,18 @@ int parse_enum_node(CParserState *state, TSNode node, const char *text, ParserTy
|
|||
}
|
||||
}
|
||||
}
|
||||
// C23 fixed underlying type, e.g. "enum E : unsigned int { ... }". The
|
||||
// grammar exposes it as the "underlying_type" field; store it on the
|
||||
// base type (RzBaseType::type), leaving it NULL for a classic enum.
|
||||
TSNode enum_underlying = ts_node_child_by_field_name(node, "underlying_type", 15);
|
||||
if (!ts_node_is_null(enum_underlying)) {
|
||||
ParserTypePair *underlying = NULL;
|
||||
if (!parse_type_node_single(state, enum_underlying, text, &underlying, false) && underlying) {
|
||||
rz_type_free(enum_pair->btype->type);
|
||||
enum_pair->btype->type = underlying->type;
|
||||
free(underlying);
|
||||
}
|
||||
}
|
||||
// If parsing successfull completed - we store the state
|
||||
if (enum_pair) {
|
||||
c_parser_base_type_store(state, name, enum_pair);
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ static char *get_type_data(Sdb *sdb, const char *type, const char *sname) {
|
|||
return members;
|
||||
}
|
||||
|
||||
static TypeFormatPair *get_enum_type(Sdb *sdb, const char *sname) {
|
||||
rz_return_val_if_fail(sdb && RZ_STR_ISNOTEMPTY(sname), NULL);
|
||||
static TypeFormatPair *get_enum_type(RzTypeDB *typedb, Sdb *sdb, const char *sname) {
|
||||
rz_return_val_if_fail(typedb && sdb && RZ_STR_ISNOTEMPTY(sname), NULL);
|
||||
|
||||
RzBaseType *base_type = rz_type_base_type_new(RZ_BASE_TYPE_KIND_ENUM);
|
||||
if (!base_type) {
|
||||
|
|
@ -76,6 +76,25 @@ static TypeFormatPair *get_enum_type(Sdb *sdb, const char *sname) {
|
|||
}
|
||||
free(members);
|
||||
|
||||
// C23 fixed underlying type, stored by save_enum() under "enum.<name>.@type".
|
||||
// The referenced type may not have been deserialized yet -- base types are read
|
||||
// from the sdb in an unspecified (hash) order -- but rz_type_parse_string_single()
|
||||
// turns an unknown name into a forward-looking identifier instead of failing, and
|
||||
// identifiers are resolved lazily by name when used (e.g. for the enum width), so
|
||||
// the load order of the underlying type relative to this enum does not matter.
|
||||
RzStrBuf utkey;
|
||||
char *underlying = sdb_get(sdb, rz_strbuf_initf(&utkey, "enum.%s.@type", sname));
|
||||
rz_strbuf_fini(&utkey);
|
||||
if (underlying) {
|
||||
char *error_msg = NULL;
|
||||
RzType *ut = rz_type_parse_string_single(typedb->parser, underlying, &error_msg);
|
||||
free(underlying);
|
||||
free(error_msg);
|
||||
if (ut) {
|
||||
base_type->type = ut;
|
||||
}
|
||||
}
|
||||
|
||||
RzStrBuf key;
|
||||
char *format = sdb_get(sdb, rz_strbuf_initf(&key, "type.%s", sname));
|
||||
rz_strbuf_fini(&key);
|
||||
|
|
@ -327,7 +346,7 @@ bool sdb_load_base_types(RzTypeDB *typedb, Sdb *sdb) {
|
|||
if (!strcmp(sdbkv_value(kv), "struct")) {
|
||||
tpair = get_struct_type(typedb, sdb, sdbkv_key(kv));
|
||||
} else if (!strcmp(sdbkv_value(kv), "enum")) {
|
||||
tpair = get_enum_type(sdb, sdbkv_key(kv));
|
||||
tpair = get_enum_type(typedb, sdb, sdbkv_key(kv));
|
||||
} else if (!strcmp(sdbkv_value(kv), "union")) {
|
||||
tpair = get_union_type(typedb, sdb, sdbkv_key(kv));
|
||||
} else if (!strcmp(sdbkv_value(kv), "typedef")) {
|
||||
|
|
@ -497,6 +516,16 @@ static void save_enum(const RzTypeDB *typedb, Sdb *sdb, const RzBaseType *type)
|
|||
sdb_set(sdb, key, rz_strbuf_get(&arglist));
|
||||
free(key);
|
||||
|
||||
// C23 fixed underlying type, e.g. "enum E : long long { ... }".
|
||||
// Stored under a key that cannot clash with a case name.
|
||||
if (type->type) {
|
||||
char *underlying = rz_type_as_string(typedb, type->type);
|
||||
if (underlying) {
|
||||
sdb_set(sdb, rz_strbuf_setf(¶m_key, "enum.%s.@type", sname), underlying);
|
||||
free(underlying);
|
||||
}
|
||||
}
|
||||
|
||||
rz_strbuf_fini(&arglist);
|
||||
rz_strbuf_fini(¶m_key);
|
||||
rz_strbuf_fini(¶m_val);
|
||||
|
|
|
|||
|
|
@ -675,7 +675,11 @@ static ut64 atomic_bitsize(const RzTypeDB *typedb, RZ_NONNULL RzBaseType *btype)
|
|||
|
||||
static ut64 enum_bitsize(const RzTypeDB *typedb, RZ_NONNULL RzBaseType *btype) {
|
||||
rz_return_val_if_fail(typedb && btype && btype->kind == RZ_BASE_TYPE_KIND_ENUM, 0);
|
||||
// FIXME: Need a proper way to determine size of enum
|
||||
// A C23 enum can fix its underlying type ("enum E : long long { ... }");
|
||||
// otherwise it defaults to the implementation's int width.
|
||||
if (btype->type) {
|
||||
return rz_type_db_get_bitsize(typedb, btype->type);
|
||||
}
|
||||
return 32;
|
||||
}
|
||||
|
||||
|
|
@ -1043,6 +1047,13 @@ static char *type_as_pretty_string(const RzTypeDB *typedb, const RzType *type, c
|
|||
case RZ_BASE_TYPE_KIND_ENUM:
|
||||
if (unfold_all || (is_anon && unfold_anon)) {
|
||||
RzTypeEnumCase *cas;
|
||||
if (btype->type) { // C23 fixed underlying type
|
||||
char *underlying = rz_type_as_string(typedb, btype->type);
|
||||
if (underlying) {
|
||||
rz_strbuf_appendf(buf, " : %s", underlying);
|
||||
free(underlying);
|
||||
}
|
||||
}
|
||||
rz_strbuf_append(buf, " {");
|
||||
if (multiline) {
|
||||
indent++; // no recursive call, so manually need to update indent
|
||||
|
|
|
|||
|
|
@ -380,6 +380,7 @@ Detailed project load info:
|
|||
project migrated from version 19 to 20.
|
||||
project migrated from version 20 to 21.
|
||||
project migrated from version 21 to 22.
|
||||
project migrated from version 22 to 23.
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
|
|
|||
|
|
@ -586,41 +586,55 @@ tec v
|
|||
te v
|
||||
te v 0x123
|
||||
te v 0x321
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
enum v : int { t = 0x123, p = 0x321 };
|
||||
enum v : int {
|
||||
t = 0x123,
|
||||
p = 0x321
|
||||
};
|
||||
enum v : int {
|
||||
t = 0x123,
|
||||
p = 0x321
|
||||
};
|
||||
t = 0x123
|
||||
p = 0x321
|
||||
t
|
||||
p
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=enhanced enum with long long underlying type
|
||||
BROKEN=1
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
td "enum w : long long { t=0x123, p=0x321 };"
|
||||
tcd w
|
||||
tc w
|
||||
tec w
|
||||
te w
|
||||
te w 0x123
|
||||
te w 0x321
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
enum v { t = 0x123, p = 0x321 };
|
||||
enum v {
|
||||
enum w : long long { t = 0x123, p = 0x321 };
|
||||
enum w : long long {
|
||||
t = 0x123,
|
||||
p = 0x321
|
||||
};
|
||||
enum v {
|
||||
t = 0x123,
|
||||
p = 0x321
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=enhanced enum with unsigned int underlying type
|
||||
BROKEN=1
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
td "enum ui : unsigned int { a=1, b=2 };"
|
||||
tcd ui
|
||||
tc ui
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
enum ui : unsigned int { a = 0x1, b = 0x2 };
|
||||
enum ui : unsigned int {
|
||||
a = 0x1,
|
||||
b = 0x2
|
||||
};
|
||||
t = 0x123
|
||||
p = 0x321
|
||||
t
|
||||
p
|
||||
enum w { t = 0x123, p = 0x321 };
|
||||
enum w {
|
||||
t = 0x123,
|
||||
p = 0x321
|
||||
};
|
||||
enum w {
|
||||
t = 0x123,
|
||||
p = 0x321
|
||||
};
|
||||
t = 0x123
|
||||
p = 0x321
|
||||
t
|
||||
p
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
|
|
|||
|
|
@ -391,7 +391,7 @@ echo ---
|
|||
echo ---
|
||||
!rz-bin -P ${RZ_FILE} | grep all_control_bits | grep -ao 0x3c09f00
|
||||
echo ---
|
||||
!rz-bin -P ${RZ_FILE} | grep "__acrt_fenv_abstract_status {"
|
||||
!rz-bin -P ${RZ_FILE} | grep "enum __acrt_fenv_abstract_status : uint32_t {"
|
||||
echo ---
|
||||
!rz-bin -P ${RZ_FILE} | grep "_towlower_internal"
|
||||
echo ---
|
||||
|
|
@ -404,7 +404,7 @@ nLength
|
|||
---
|
||||
0x3c09f00
|
||||
---
|
||||
enum __acrt_fenv_abstract_status {
|
||||
enum __acrt_fenv_abstract_status : uint32_t {
|
||||
---
|
||||
0x00066da4 4366 .text _towlower_internal
|
||||
---
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <rz_project.h>
|
||||
|
||||
#include "../unit/minunit.h"
|
||||
#include "test_config.h"
|
||||
#include "rz_config.h"
|
||||
#include "sdb.h"
|
||||
|
||||
|
|
@ -691,6 +692,42 @@ static bool test_migrate_v21_v22_gadget_config() {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_migrate_v22_v23_enum() {
|
||||
RzProject *prj = rz_project_load_file_raw("prj/v22-enum.rzdb");
|
||||
mu_assert_notnull(prj, "load raw project");
|
||||
RzSerializeResultInfo *res = rz_serialize_result_info_new();
|
||||
bool s = rz_project_migrate_v22_v23(prj, res);
|
||||
mu_assert_true(s, "v22->v23 migrate success");
|
||||
|
||||
Sdb *core_db = sdb_ns(prj, "core", false);
|
||||
mu_assert_notnull(core_db, "core ns");
|
||||
Sdb *analysis_db = sdb_ns(core_db, "analysis", false);
|
||||
mu_assert_notnull(analysis_db, "analysis ns");
|
||||
Sdb *types_db = sdb_ns(analysis_db, "types", false);
|
||||
mu_assert_notnull(types_db, "types ns");
|
||||
|
||||
// The migration is additive: a classic enum keeps its cases and gains no
|
||||
// "enum.<name>.@type" key (that key is only written for a fixed underlying type).
|
||||
mu_assert_streq_free(sdb_get(types_db, "Color"), "enum", "classic enum kind preserved");
|
||||
mu_assert_null(sdb_get(types_db, "enum.Color.@type"), "classic enum has no underlying-type key");
|
||||
|
||||
// It must still deserialize into a classic enum (no underlying type).
|
||||
RzTypeDB *typedb = rz_type_db_new();
|
||||
mu_assert_notnull(typedb, "type db new");
|
||||
rz_type_db_init(typedb, TEST_BUILD_TYPES_DIR, "x86", 64, "linux");
|
||||
mu_assert_true(rz_serialize_types_load(types_db, typedb, NULL), "types deserialize");
|
||||
RzBaseType *color = rz_type_db_get_base_type(typedb, "Color");
|
||||
mu_assert_notnull(color, "Color base type present");
|
||||
mu_assert_eq(color->kind, RZ_BASE_TYPE_KIND_ENUM, "Color is an enum");
|
||||
mu_assert_null(color->type, "classic enum has no underlying type");
|
||||
mu_assert_eq(rz_vector_len(&color->enum_data.cases), 3, "three enum cases preserved");
|
||||
rz_type_db_free(typedb);
|
||||
|
||||
rz_serialize_result_info_free(res);
|
||||
rz_project_free(prj);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
/// Load project of given version from file into core and check the log for migration success messages
|
||||
#define BEGIN_LOAD_TEST(core, version, file) \
|
||||
do { \
|
||||
|
|
@ -1127,6 +1164,7 @@ int all_tests() {
|
|||
mu_run_test(test_migrate_v18_v19_str_config);
|
||||
mu_run_test(test_migrate_v20_v21_debase64);
|
||||
mu_run_test(test_migrate_v21_v22_gadget_config);
|
||||
mu_run_test(test_migrate_v22_v23_enum);
|
||||
mu_run_test(test_load_v1_noreturn);
|
||||
mu_run_test(test_load_v1_noreturn_empty);
|
||||
mu_run_test(test_load_v1_unknown_type);
|
||||
|
|
|
|||
91
test/prj/v22-enum.rzdb
Normal file
91
test/prj/v22-enum.rzdb
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/
|
||||
type=rizin rz-db project
|
||||
version=22
|
||||
|
||||
/core
|
||||
blocksize=0x100
|
||||
offset=0x80483d0
|
||||
|
||||
/core/analysis
|
||||
|
||||
/core/analysis/blocks
|
||||
|
||||
/core/analysis/callables
|
||||
|
||||
/core/analysis/cc
|
||||
|
||||
/core/analysis/classes
|
||||
|
||||
/core/analysis/classes/attrs
|
||||
|
||||
/core/analysis/functions
|
||||
|
||||
/core/analysis/hints
|
||||
|
||||
/core/analysis/imports
|
||||
|
||||
/core/analysis/meta
|
||||
|
||||
/core/analysis/meta/spaces
|
||||
name=CS
|
||||
spacestack=["*"]
|
||||
|
||||
/core/analysis/meta/spaces/spaces
|
||||
bin=s
|
||||
|
||||
/core/analysis/noreturn
|
||||
|
||||
/core/analysis/pins
|
||||
|
||||
/core/analysis/typelinks
|
||||
|
||||
/core/analysis/types
|
||||
Color=enum
|
||||
enum.Color=RED,GREEN,BLUE
|
||||
enum.Color.0x0=RED
|
||||
enum.Color.0x1=GREEN
|
||||
enum.Color.0x2=BLUE
|
||||
enum.Color.BLUE=0x2
|
||||
enum.Color.GREEN=0x1
|
||||
enum.Color.RED=0x0
|
||||
|
||||
/core/analysis/vars
|
||||
|
||||
/core/analysis/xrefs
|
||||
|
||||
/core/analysis/zigns
|
||||
|
||||
/core/analysis/zigns/spaces
|
||||
name=zs
|
||||
spacestack=["*"]
|
||||
|
||||
/core/analysis/zigns/spaces/spaces
|
||||
|
||||
/core/config
|
||||
|
||||
/core/file
|
||||
relative=../bins/elf/crackme0x05
|
||||
|
||||
/core/flags
|
||||
base=0
|
||||
realnames=0
|
||||
|
||||
/core/flags/flags
|
||||
|
||||
/core/flags/spaces
|
||||
name=fs
|
||||
spacestack=["*"]
|
||||
|
||||
/core/flags/spaces/spaces
|
||||
classes=s
|
||||
imports=s
|
||||
relocs=s
|
||||
sections=s
|
||||
segments=s
|
||||
strings=s
|
||||
symbols=s
|
||||
symbols.sections=s
|
||||
|
||||
/core/flags/tags
|
||||
|
||||
/core/flags/zones
|
||||
|
|
@ -266,9 +266,70 @@ bool test_types_load() {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
bool test_types_load_enum_underlying_forward() {
|
||||
// Regression test for the PR #6433 review concern: an enum may fix its
|
||||
// underlying type ("enum E : T"), serialized as "enum.<name>.@type". Base
|
||||
// types are read from the sdb in an arbitrary (hash) order, so T may not have
|
||||
// been loaded yet when the enum is deserialized. The underlying type must be
|
||||
// kept as a lazily-resolved identifier (not dropped) and must resolve once the
|
||||
// referenced type is available.
|
||||
RzTypeDB *typedb = rz_type_db_new();
|
||||
rz_type_db_set_cpu(typedb, "x86");
|
||||
rz_type_db_set_bits(typedb, 64);
|
||||
rz_type_db_set_os(typedb, "linux");
|
||||
const char *types_dir = TEST_BUILD_TYPES_DIR;
|
||||
rz_type_db_init(typedb, types_dir, "x86", 64, "linux");
|
||||
|
||||
Sdb *db = sdb_new0();
|
||||
// An enum whose underlying type is a user typedef defined in the same sdb.
|
||||
// Whichever of the two records is deserialized first, the underlying type must
|
||||
// end up resolved to the typedef's width.
|
||||
sdb_set(db, "narciso", "enum");
|
||||
sdb_set(db, "enum.narciso", "GILLIAN,JAMIE");
|
||||
sdb_set(db, "enum.narciso.GILLIAN", "0x1");
|
||||
sdb_set(db, "enum.narciso.JAMIE", "0x2");
|
||||
sdb_set(db, "enum.narciso.0x1", "GILLIAN");
|
||||
sdb_set(db, "enum.narciso.0x2", "JAMIE");
|
||||
sdb_set(db, "enum.narciso.@type", "metal_gear_t");
|
||||
sdb_set(db, "metal_gear_t", "typedef");
|
||||
sdb_set(db, "typedef.metal_gear_t", "int64_t");
|
||||
// An enum whose underlying type is absent from the sdb entirely: the strongest
|
||||
// form of "not loaded yet". The underlying must still be kept as an identifier.
|
||||
sdb_set(db, "raiden", "enum");
|
||||
sdb_set(db, "enum.raiden", "SNAKE");
|
||||
sdb_set(db, "enum.raiden.SNAKE", "0x0");
|
||||
sdb_set(db, "enum.raiden.0x0", "SNAKE");
|
||||
sdb_set(db, "enum.raiden.@type", "ghost_t");
|
||||
|
||||
mu_assert_true(rz_serialize_types_load(db, typedb, NULL), "types load");
|
||||
|
||||
// enum referencing a typedef present in the same sdb: underlying kept and
|
||||
// resolved, independent of the deserialization order of the two records.
|
||||
RzBaseType *narciso = rz_type_db_get_base_type(typedb, "narciso");
|
||||
mu_assert_notnull(narciso, "narciso loaded");
|
||||
mu_assert_eq(narciso->kind, RZ_BASE_TYPE_KIND_ENUM, "narciso is an enum");
|
||||
mu_assert_notnull(narciso->type, "narciso keeps its underlying type");
|
||||
mu_assert_eq(rz_type_db_get_bitsize(typedb, narciso->type), 64, "underlying resolves to 64-bit");
|
||||
mu_assert_eq(rz_type_db_base_get_bitsize(typedb, narciso), 64, "enum sized from its underlying type");
|
||||
|
||||
// enum referencing a type that is not in the sdb at all: the underlying type is
|
||||
// still kept as an (as-yet-unresolved) identifier, not dropped.
|
||||
RzBaseType *raiden = rz_type_db_get_base_type(typedb, "raiden");
|
||||
mu_assert_notnull(raiden, "raiden loaded");
|
||||
mu_assert_eq(raiden->kind, RZ_BASE_TYPE_KIND_ENUM, "raiden is an enum");
|
||||
mu_assert_notnull(raiden->type, "raiden keeps its underlying type even when unresolved");
|
||||
mu_assert_eq(raiden->type->kind, RZ_TYPE_KIND_IDENTIFIER, "underlying kept as identifier");
|
||||
mu_assert_streq(raiden->type->identifier.name, "ghost_t", "underlying identifier name kept");
|
||||
|
||||
sdb_free(db);
|
||||
rz_type_db_free(typedb);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests() {
|
||||
mu_run_test(test_types_save);
|
||||
mu_run_test(test_types_load);
|
||||
mu_run_test(test_types_load_enum_underlying_forward);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -416,6 +416,45 @@ static bool test_enum_types(void) {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_enum_underlying_type(void) {
|
||||
RzTypeDB *typedb = rz_type_db_new();
|
||||
mu_assert_notnull(typedb, "Couldn't create new RzTypeDB");
|
||||
const char *types_dir = TEST_BUILD_TYPES_DIR;
|
||||
rz_type_db_init(typedb, types_dir, "x86", 64, "linux");
|
||||
|
||||
// C23 enum with a fixed underlying type. A single-token primitive type is
|
||||
// used here since that is what the bundled grammar revision already parses.
|
||||
char *error_msg = NULL;
|
||||
RzType *ttype = rz_type_parse_string_single(typedb->parser, "enum EU : uint64_t { A = 1, B = 2 };", &error_msg);
|
||||
mu_assert_notnull(ttype, "enum with underlying type parses");
|
||||
mu_assert_null(error_msg, "no parse error");
|
||||
|
||||
RzBaseType *base = rz_type_db_get_base_type(typedb, "EU");
|
||||
mu_assert_notnull(base, "EU base type exists");
|
||||
mu_assert_eq(RZ_BASE_TYPE_KIND_ENUM, base->kind, "EU is an enum");
|
||||
mu_assert_notnull(base->type, "underlying type stored on the base type");
|
||||
mu_assert_streq_free(rz_type_as_string(typedb, base->type), "uint64_t", "underlying type is uint64_t");
|
||||
mu_assert_streq_free(rz_type_db_base_type_as_string(typedb, base), "enum EU : uint64_t { A = 0x1, B = 0x2 }", "enum renders its underlying type");
|
||||
// enum_bitsize() now derives the width from the underlying type via
|
||||
// rz_type_db_base_get_bitsize(); the concrete value is exercised in the
|
||||
// integration tests since this unit harness does not load atomic widths.
|
||||
rz_type_free(ttype);
|
||||
|
||||
// A classic enum keeps a NULL underlying type and the default int width
|
||||
error_msg = NULL;
|
||||
RzType *plain = rz_type_parse_string_single(typedb->parser, "enum EC { X = 7 };", &error_msg);
|
||||
mu_assert_notnull(plain, "classic enum parses");
|
||||
RzBaseType *bc = rz_type_db_get_base_type(typedb, "EC");
|
||||
mu_assert_notnull(bc, "EC base type exists");
|
||||
mu_assert_null(bc->type, "classic enum has no underlying type");
|
||||
mu_assert_streq_free(rz_type_db_base_type_as_string(typedb, bc), "enum EC { X = 0x7 }", "classic enum renders without an underlying type");
|
||||
mu_assert_eq(rz_type_db_base_get_bitsize(typedb, bc), 32, "classic enum defaults to 32 bits");
|
||||
rz_type_free(plain);
|
||||
|
||||
rz_type_db_free(typedb);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_const_types(void) {
|
||||
RzTypeDB *typedb = rz_type_db_new();
|
||||
mu_assert_notnull(typedb, "Couldn't create new RzTypeDB");
|
||||
|
|
@ -1969,6 +2008,7 @@ int all_tests() {
|
|||
mu_run_test(test_type_as_string);
|
||||
mu_run_test(test_type_as_pretty_string);
|
||||
mu_run_test(test_enum_types);
|
||||
mu_run_test(test_enum_underlying_type);
|
||||
mu_run_test(test_const_types);
|
||||
mu_run_test(test_array_types);
|
||||
mu_run_test(test_single_typedef_aliases);
|
||||
|
|
|
|||
Loading…
Reference in a new issue