From bffd2d94ad072b9b6fe880ca96ce9a14d5fb4898 Mon Sep 17 00:00:00 2001
From: Martijn Reicher
Set optionality on any element that makes sense to be optional.
", - .callback = annotate_optional }, -#endif + "Indicates that the annotated member may be in a NULL state, not" + "containing any value.
", + .callback = &annotate_optional }, { .syntax = "@annotation value { any value; };", .summary = "Set a constant value to any element that may be given a constant "
diff --git a/src/idl/tests/annotation.c b/src/idl/tests/annotation.c
index c3f377e4ed..90b7ba4928 100644
--- a/src/idl/tests/annotation.c
+++ b/src/idl/tests/annotation.c
@@ -45,6 +45,62 @@ parse_string(uint32_t flags, const char *str, idl_pstate_t **pstatep)
return ret;
}
+typedef struct optional_test {
+ const char *str;
+ idl_retcode_t ret;
+ bool optionals[16];
+} optional_test_t;
+
+static void test_optional(optional_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_node_t *node;
+ int nstructs = 0;
+ IDL_FOREACH(node, pstate->root) {
+ if (!idl_is_struct(node))
+ continue;
+ nstructs++;
+ idl_struct_t *s = (idl_struct_t *)node;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(s);
+ CU_ASSERT_FATAL(idl_is_struct(s));
+ assert(s);
+ idl_member_t *m = NULL;
+ int n = 0;
+ IDL_FOREACH(m, s->members) {
+ CU_ASSERT_EQUAL(m->optional.value, test.optionals[n]);
+ n++;
+ }
+ }
+ CU_ASSERT_EQUAL(nstructs, 1);
+ idl_delete_pstate(pstate);
+ }
+}
+
+CU_Test(idl_annotation, optional)
+{
+ static const optional_test_t tests[] = {
+ {"struct s { char c; };", IDL_RETCODE_OK, {false} }, //default (not optional)
+ {"struct s { @optional char c; };", IDL_RETCODE_OK, {true} }, //implicit true
+ {"struct s { @optional(false) char c; };", IDL_RETCODE_OK, {false} }, //explicit false
+ {"struct s { @optional(true) char c; };", IDL_RETCODE_OK, {true} }, //explicit true
+ {"struct s { @optional(true) char c_1, c_2; char c_3; };", IDL_RETCODE_OK, {true, false} }, //set on both declarators
+ {"struct s { @optional sequence Specify a data member is part of the key for the object whose type "
"is the constructed data type owning this element. Specify the value with which the annotated member should be default"
+ "initialized.
Specify a default value for the annotated element.
", - .callback = annotate_default }, { .syntax = "@annotation range { any min; any max; };", .summary = "Specify a range of allowed value for the annotated element.
", diff --git a/src/idl/src/tree.c b/src/idl/src/tree.c index 6954ec838e..3bb7135a03 100644 --- a/src/idl/src/tree.c +++ b/src/idl/src/tree.c @@ -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; diff --git a/src/idl/tests/annotation.c b/src/idl/tests/annotation.c index 90b7ba4928..3bf916e3e2 100644 --- a/src/idl/tests/annotation.c +++ b/src/idl/tests/annotation.c @@ -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; From 010289ea1db92dbe894d3733e43c3dc3cafcc4ad Mon Sep 17 00:00:00 2001 From: Martijn ReicherSpecify the value with which the annotated member should be default" "initialized.
", .callback = &annotate_default }, + { .syntax = "@annotation default_literal { };", + .summary = + "Explicity sets the default value for an enum to the annotated enumerator" + "instead of the first entry.
", + .callback = &annotate_default_literal }, #if 0 { .syntax = "@annotation must_understand { boolean value default TRUE; };", .summary = diff --git a/src/idl/src/tree.c b/src/idl/src/tree.c index 3bb7135a03..dd2fb46342 100644 --- a/src/idl/src/tree.c +++ b/src/idl/src/tree.c @@ -2029,6 +2029,23 @@ idl_create_enum( ret = IDL_RETCODE_SEMANTIC_ERROR; goto err_clash; } + if (idl_mask(e1) == IDL_DEFAULT_ENUMERATOR) { + if (node->default_enumerator) { + idl_error(pstate, idl_location(e1), + "Assigning default to enumerator '%s' clashes with '%s' already being set as default.", + e1->name->identifier, node->default_enumerator->name->identifier); + ret = IDL_RETCODE_SEMANTIC_ERROR; + goto err_defaults; + } else { + node->default_enumerator = e1; + } + } + } + + //fallback to the first entry + if (!node->default_enumerator) { + node->default_enumerator = enumerators; + node->default_enumerator->node.mask = IDL_IMPLICIT_DEFAULT_ENUMERATOR; } if ((ret = idl_declare(pstate, kind, name, node, NULL, NULL))) @@ -2038,6 +2055,7 @@ idl_create_enum( return IDL_RETCODE_OK; err_declare: err_clash: +err_defaults: free(node); err_alloc: return ret; @@ -2075,7 +2093,9 @@ static void *iterate_enumerator(const void *ptr, const void *cur) static const char *describe_enumerator(const void *ptr) { (void)ptr; - assert(idl_mask(ptr) == IDL_ENUMERATOR); + assert(idl_mask(ptr) == IDL_ENUMERATOR + || idl_mask(ptr) == IDL_DEFAULT_ENUMERATOR + || idl_mask(ptr) == IDL_IMPLICIT_DEFAULT_ENUMERATOR); return "enumerator"; } diff --git a/src/idl/tests/annotation.c b/src/idl/tests/annotation.c index 3bf916e3e2..f096b95c03 100644 --- a/src/idl/tests/annotation.c +++ b/src/idl/tests/annotation.c @@ -187,6 +187,63 @@ CU_Test(idl_annotation, idl_default) } +typedef struct enum_default_test { + const char *str; + idl_retcode_t ret; + uint32_t default_index; + const char *default_name; + uint32_t default_mask; +} enum_default_test_t; + +static void test_enum_default(enum_default_test_t test) { + idl_pstate_t *pstate = NULL; + idl_retcode_t ret = parse_string(IDL_FLAG_ANNOTATIONS, test.str, &pstate); + CU_ASSERT_EQUAL(ret, test.ret); + if (test.ret == ret + && ret == IDL_RETCODE_OK) { + CU_ASSERT_PTR_NOT_NULL_FATAL(pstate); + assert(pstate); + idl_enum_t *e = (idl_enum_t *)pstate->root; + CU_ASSERT(idl_is_enum(e)); + if (idl_is_enum(e)) { + assert(e); + idl_enumerator_t *en = e->default_enumerator; + CU_ASSERT_PTR_NOT_NULL_FATAL(en); + if (en) { + CU_ASSERT_EQUAL(en->value.value, test.default_index); + CU_ASSERT_EQUAL(idl_mask(en), test.default_mask); + CU_ASSERT_STRING_EQUAL(en->name->identifier, test.default_name); + } + + IDL_FOREACH(en, e->enumerators) { + if (en == e->default_enumerator) { + CU_ASSERT_EQUAL(idl_mask(en), test.default_mask); + } else { + CU_ASSERT_EQUAL(idl_mask(en), IDL_ENUMERATOR); + } + } + } + + idl_delete_pstate(pstate); + } +} + +CU_Test(idl_annotation, default_literal) +{ + static const enum_default_test_t tests[] = { + {"enum e { e_0, e_1, e_2, e_3 };", IDL_RETCODE_OK, 0, "e_0", IDL_IMPLICIT_DEFAULT_ENUMERATOR }, //implicit defaults to the first entry + {"enum e { e_0, @default_literal e_1, e_2, e_3 };", IDL_RETCODE_OK, 1, "e_1", IDL_DEFAULT_ENUMERATOR }, //setting an explicit default through the annotation + {"enum e { @value(123) e_0, @default_literal e_1, e_2, e_3 };", IDL_RETCODE_OK, 124, "e_1", IDL_DEFAULT_ENUMERATOR }, //starting at a different id + {"enum e { e_0, e_1, @default_literal @default_literal e_2, e_3 };", IDL_RETCODE_OK, 2, "e_2", IDL_DEFAULT_ENUMERATOR }, //re-annotate + {"enum e { e_0, @default_literal e_1, @default_literal e_2, e_3 };", IDL_RETCODE_SEMANTIC_ERROR, 0, NULL, 0 }, //more than one enumerator set as default + {"struct s { @default_literal long l; double d; };\n", IDL_RETCODE_SEMANTIC_ERROR, 0, NULL, 0 }, //this annotation is only allowed on enumerator fields + }; + + for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + test_enum_default(tests[i]); + } +} + CU_Test(idl_annotation, key) { idl_retcode_t ret;