Skip to content

Commit

Permalink
Add @default parsing
Browse files Browse the repository at this point in the history
- Add parsing of @default annotation fields
- Reject mixing @default and @optional fields
- Add idl_has_default function returns a pointer to the idl_literal_t
  representing the default value assigned to the node, or NULL if there
  is none
- Add unittests for @default annotation

Signed-off-by: Martijn Reicher <martijn.reicher@adlinktech.com>
  • Loading branch information
reicheratwork authored and k0ekk0ek committed Sep 24, 2021
1 parent b113222 commit e4ec4da
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/idl/include/idl/tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ struct idl_member {
/* metadata */
IDL_ANNOTATABLE(bool) key;
IDL_ANNOTATABLE(bool) optional;
IDL_ANNOTATABLE(const idl_literal_t*) value;
IDL_ANNOTATABLE(uint32_t) id;
};

Expand Down Expand Up @@ -482,6 +483,7 @@ IDL_EXPORT bool idl_identifier_is(const void *node, const char *identifier);
IDL_EXPORT const idl_name_t *idl_name(const void *node);
IDL_EXPORT uint32_t idl_array_size(const void *node);
IDL_EXPORT uint32_t idl_bound(const void *node);
IDL_EXPORT const idl_literal_t *idl_default_value(const void *node);

/* navigation */
IDL_EXPORT void *idl_ancestor(const void *node, size_t levels);
Expand Down
58 changes: 54 additions & 4 deletions src/idl/src/annotation.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ annotate_optional(
idl_error(pstate, idl_location(annotation_appl),
"@optional cannot be assigned to key members");
return IDL_RETCODE_SEMANTIC_ERROR;
} else if (mem->value.annotation) {
idl_error(pstate, idl_location(annotation_appl),
"@optional cannot be assigned to members with explicit default values");
return IDL_RETCODE_SEMANTIC_ERROR;
}

if (annotation_appl->parameters) {
Expand Down Expand Up @@ -345,6 +349,51 @@ set_nested(
return IDL_RETCODE_OK;
}

static idl_retcode_t
annotate_default(
idl_pstate_t *pstate,
idl_annotation_appl_t *annotation_appl,
idl_node_t *node)
{
idl_const_expr_t *value;
idl_member_t *mem = (idl_member_t*)node;
idl_type_spec_t *mem_spec = mem->type_spec;

assert(annotation_appl);
assert(annotation_appl->parameters);

if (!idl_is_member(node)) {
idl_error(pstate, idl_location(annotation_appl),
"@default can only be assigned to members");
return IDL_RETCODE_SEMANTIC_ERROR;
} else if (mem->optional.value) {
idl_error(pstate, idl_location(annotation_appl),
"@default cannot be set on optional members");
return IDL_RETCODE_SEMANTIC_ERROR;
}

value = annotation_appl->parameters->const_expr;
assert(idl_is_literal(value));

/*check whether type of literal matches and falls inside spec of member*/
idl_type_t mem_type = idl_type(mem_spec);
if (mem_type != idl_type(value)) {
idl_retcode_t ret = IDL_RETCODE_OK;
idl_literal_t *literal = NULL;
if ((ret = idl_evaluate(pstate, value, mem_type, &literal)))
return ret;

assert(literal);
annotation_appl->parameters->const_expr = literal;
literal->node.parent = (idl_node_t*)annotation_appl->parameters;
}

((idl_member_t *)node)->value.annotation = annotation_appl;
((idl_member_t *)node)->value.value = annotation_appl->parameters->const_expr;

return IDL_RETCODE_OK;
}

static idl_retcode_t
annotate_nested(
idl_pstate_t *pstate,
Expand Down Expand Up @@ -504,17 +553,18 @@ static const idl_builtin_annotation_t annotations[] = {
"<p>Specify a data member is part of the key for the object whose type "
"is the constructed data type owning this element.</p>",
.callback = annotate_key },
{ .syntax = "@annotation default { any value; };",
.summary =
"<p>Specify the value with which the annotated member should be default"
"initialized.</p>",
.callback = &annotate_default },
#if 0
{ .syntax = "@annotation must_understand { boolean value default TRUE; };",
.summary =
"<p>Specify the data member must be understood by any application "
"making use of that piece of data.</p>",
.callback = annotate_must_understand },
/* units and ranges */
{ .syntax = "@annotation default { any value; };",
.summary =
"<p>Specify a default value for the annotated element.</p>",
.callback = annotate_default },
{ .syntax = "@annotation range { any min; any max; };",
.summary =
"<p>Specify a range of allowed value for the annotated element.</p>",
Expand Down
13 changes: 13 additions & 0 deletions src/idl/src/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,19 @@ uint32_t idl_bound(const void *node)
return 0u;
}

const idl_literal_t *idl_default_value(const void *node)
{
if (idl_is_member(node)) {
return ((const idl_member_t*)node)->value.value;
} else if (idl_is_declarator(node)) {
const idl_node_t *parent = idl_parent(node);
if (idl_is_member(parent))
return idl_default_value(parent);
}

return NULL;
}

bool idl_is_sequence(const void *ptr)
{
idl_mask_t mask;
Expand Down
86 changes: 86 additions & 0 deletions src/idl/tests/annotation.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,92 @@ CU_Test(idl_annotation, optional)
}
}

typedef struct idl_default_test {
const char *str;
idl_retcode_t ret;
bool has_default;
idl_type_t default_type;
const void *default_val_ptr;
} idl_default_test_t;

static void test_default(
idl_default_test_t test)
{
idl_pstate_t *pstate = NULL;
idl_retcode_t ret = parse_string(IDL_FLAG_ANNOTATIONS, test.str, &pstate);
CU_ASSERT_EQUAL_FATAL(ret, test.ret);
if (pstate) {
idl_struct_t *s = (idl_struct_t *)pstate->root;
CU_ASSERT_FATAL(idl_is_struct(s));
assert(s);
idl_member_t *m = NULL;
IDL_FOREACH(m, s->members) {
const idl_literal_t *def = idl_default_value(m);
if (test.has_default) {
CU_ASSERT_EQUAL_FATAL(idl_type(def), test.default_type);
switch (test.default_type) {
case IDL_LONG:
CU_ASSERT_EQUAL(def->value.int32, *(const int32_t*)test.default_val_ptr);
break;
case IDL_ULONG:
CU_ASSERT_EQUAL(def->value.uint32, *(const uint32_t*)test.default_val_ptr);
break;
case IDL_DOUBLE:
CU_ASSERT_EQUAL(def->value.dbl, *(const double*)test.default_val_ptr);
break;
case IDL_CHAR:
CU_ASSERT_EQUAL(def->value.chr, *(const char*)test.default_val_ptr);
break;
case IDL_STRING:
CU_ASSERT_STRING_EQUAL(def->value.str, *(const char**)test.default_val_ptr);
break;
case IDL_BOOL:
CU_ASSERT_EQUAL(def->value.bln, *(const bool*)test.default_val_ptr);
break;
default:
break;
}
} else {
CU_ASSERT_PTR_NULL_FATAL(def);
}
}
idl_delete_pstate(pstate);
}
}

CU_Test(idl_annotation, idl_default)
{
static const int32_t t1 = -123456789;
static const double t2 = 987.654321;
static const char t3 = 'a';
static const bool t4 = true;
static const char *t5 = "hello world!";
static const uint32_t t6 = 123456789;
static const idl_default_test_t tests[] = {
{"struct s { long l; };", IDL_RETCODE_OK, false, IDL_NULL, NULL}, //no default whatsoever
{"struct s { @default(-123456789) long l; };", IDL_RETCODE_OK, true, IDL_LONG, &t1}, //default long
{"struct s { @default(987.654321) double d; };", IDL_RETCODE_OK, true, IDL_DOUBLE, &t2}, //default double
{"struct s { @default('a') char c; };", IDL_RETCODE_OK, true, IDL_CHAR, &t3}, //default char
{"struct s { @default(true) boolean b; };", IDL_RETCODE_OK, true, IDL_BOOL, &t4}, //default bool
{"struct s { @default(\"hello world!\") string str; };", IDL_RETCODE_OK, true, IDL_STRING, &t5}, //default string
{"struct s { @default(123456789) unsigned long l; };", IDL_RETCODE_OK, true, IDL_ULONG, &t6}, //default unsigned long
{"struct s { @default(123) @optional long l; };", IDL_RETCODE_SEMANTIC_ERROR, false, IDL_NULL, NULL}, //mixing default and optional
{"struct s { @default long l; };", IDL_RETCODE_SEMANTIC_ERROR, false, IDL_NULL, NULL}, //misssing parameter
{"struct s { @default(123) string str; };", IDL_RETCODE_ILLEGAL_EXPRESSION, false, IDL_NULL, NULL}, //parameter type mismatch (int vs string)
{"struct s { @default(\"false\") boolean b; };", IDL_RETCODE_ILLEGAL_EXPRESSION, false, IDL_NULL, NULL}, //parameter type mismatch (string vs bool)
{"struct s { @default(123) boolean b; };", IDL_RETCODE_ILLEGAL_EXPRESSION, false, IDL_NULL, NULL}, //parameter type mismatch (int vs bool)
{"struct s { @default(-123) unsigned long l; };", IDL_RETCODE_OUT_OF_RANGE, false, IDL_NULL, NULL}, //parameter type mismatch (unsigned vs signed)
/* skipping this test as idl_create_annotation_appl leaks memory if idl_resolve cannot resolve the scoped name (https://github.com/eclipse-cyclonedds/cyclonedds/issues/950)
{"@default(e_0) enum e { e_0, e_1, e_2, e_3 };", IDL_RETCODE_SEMANTIC_ERROR, false, IDL_NULL, NULL} //setting default on enums is done through @default_literal
*/
};

for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
test_default(tests[i]);
}

}

CU_Test(idl_annotation, key)
{
idl_retcode_t ret;
Expand Down

0 comments on commit e4ec4da

Please sign in to comment.