schema tree FEATURE pre-compile identities
That way they can be referenced even if not
in an implemented module. Added error when
instantiated identity (or a default identityref)
is found in a non-implemented module.
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 3ebcc0b..ed4162d 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -1265,7 +1265,7 @@
char *errmsg = NULL;
const struct lys_module *mod;
LY_ARRAY_SIZE_TYPE u;
- struct lysc_ident *ident;
+ struct lysc_ident *ident, *identities;
if (options & LY_TYPE_OPTS_SECOND_CALL) {
return LY_SUCCESS;
@@ -1292,17 +1292,27 @@
asprintf(&errmsg, "Invalid identityref \"%.*s\" value - unable to map prefix to YANG schema.", (int)value_len, value);
goto error;
}
- LY_ARRAY_FOR(mod->compiled->identities, u) {
- ident = &mod->compiled->identities[u]; /* shortcut */
+ if (mod->compiled) {
+ identities = mod->compiled->identities;
+ } else {
+ identities = mod->dis_identities;
+ }
+ LY_ARRAY_FOR(identities, u) {
+ ident = &identities[u]; /* shortcut */
if (!ly_strncmp(ident->name, id_name, id_len)) {
/* we have match */
break;
}
}
- if (u == LY_ARRAY_SIZE(mod->compiled->identities)) {
+ if (u == LY_ARRAY_SIZE(identities)) {
/* no match */
asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity not found.", (int)value_len, value);
goto error;
+ } else if (!mod->compiled) {
+ /* non-implemented module */
+ asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
+ (int)value_len, value, mod->name);
+ goto error;
}
/* check that the identity matches some of the type's base identities */
diff --git a/src/tree_schema.c b/src/tree_schema.c
index d63ab06..4323c3f 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -929,8 +929,9 @@
}
if (!mod->implemented) {
- /* pre-compile features of the module */
- LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, mod->parsed->features, &mod->off_features), error);
+ /* pre-compile features and identities of the module */
+ LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, mod->parsed->features, &mod->dis_features), error);
+ LY_CHECK_GOTO(lys_identity_precompile(NULL, ctx, mod, mod->parsed->identities, &mod->dis_identities), error);
}
if (latest) {
@@ -962,8 +963,9 @@
goto error_ctx;
}
if (!mod->implemented) {
- /* pre-compile features of the submodule */
- LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, inc->submodule->features, &mod->off_features), error);
+ /* pre-compile features and identities of the submodule */
+ LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, inc->submodule->features, &mod->dis_features), error);
+ LY_CHECK_GOTO(lys_identity_precompile(NULL, ctx, mod, inc->submodule->identities, &mod->dis_identities), error);
}
}
mod->parsed->parsing = 0;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 6bb16ee..a22dd2f 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1819,12 +1819,16 @@
struct lysp_module *parsed; /**< Simply parsed (unresolved) YANG schema tree */
struct lysc_module *compiled; /**< Compiled and fully validated YANG schema tree for data parsing.
Available only for implemented modules. */
- struct lysc_feature *off_features;/**< List of pre-compiled features of the module in non implemented modules ([sized array](@ref sizedarrays)).
+ struct lysc_feature *dis_features;/**< List of pre-compiled features in a non implemented module ([sized array](@ref sizedarrays)).
These features are always disabled and cannot be enabled until the module
- become implemented. The features are present in this form to allow their linkage
+ is implemented. The features are present in this form to allow their linkage
from if-feature statements of the compiled schemas and their proper use in case
the module became implemented in future (no matter if implicitly via augment/deviate
or explicitly via ly_ctx_module_implement()). */
+ struct lysc_ident *dis_identities;/**< List of pre-compiled identities in a non-implemented module ([sized array](@ref sizedarrays))
+ These identities cannot be instantiated in data (in identityrefs) until
+ the module is implemented but can be linked by identities in implemented
+ modules. */
uint8_t implemented; /**< flag if the module is implemented, not just imported. The module is implemented if
the flag has non-zero value. Specific values are used internally:
1 - implemented module
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index c5e56a5..14dafbc 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -440,7 +440,7 @@
/* module is implemented so there is already the compiled schema */
flist = mod->compiled->features;
} else {
- flist = mod->off_features;
+ flist = mod->dis_features;
}
LY_ARRAY_FOR(flist, u) {
f = &flist[u];
@@ -948,36 +948,52 @@
return ret;
}
-/**
- * @brief Compile information from the identity statement
- *
- * The backlinks to the identities derived from this one are supposed to be filled later via lys_compile_identity_bases().
- *
- * @param[in] ctx Compile context.
- * @param[in] ident_p The parsed identity statement structure.
- * @param[in] idents List of so far compiled identities to check the name uniqueness.
- * @param[in,out] ident Prepared (empty) compiled identity structure to fill.
- * @return LY_ERR value.
- */
-static LY_ERR
-lys_compile_identity(struct lysc_ctx *ctx, struct lysp_ident *ident_p, struct lysc_ident *idents, struct lysc_ident *ident)
+LY_ERR
+lys_identity_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_ident *identities_p, struct lysc_ident **identities)
{
- unsigned int u;
+ LY_ARRAY_SIZE_TYPE offset = 0, u, v;
+ struct lysc_ctx context = {0};
LY_ERR ret = LY_SUCCESS;
- lysc_update_path(ctx, NULL, ident_p->name);
+ assert(ctx_sc || ctx);
- COMPILE_CHECK_UNIQUENESS_ARRAY(ctx, idents, name, ident, "identity", ident_p->name);
- DUP_STRING(ctx->ctx, ident_p->name, ident->name);
- DUP_STRING(ctx->ctx, ident_p->dsc, ident->dsc);
- DUP_STRING(ctx->ctx, ident_p->ref, ident->ref);
- ident->module = ctx->mod;
- COMPILE_ARRAY_GOTO(ctx, ident_p->iffeatures, ident->iffeatures, u, lys_compile_iffeature, ret, done);
- /* backlings (derived) can be added no sooner than when all the identities in the current module are present */
- COMPILE_EXTS_GOTO(ctx, ident_p->exts, ident->exts, ident, LYEXT_PAR_IDENT, ret, done);
- ident->flags = ident_p->flags;
+ if (!ctx_sc) {
+ context.ctx = ctx;
+ context.mod = module;
+ context.path_len = 1;
+ context.path[0] = '/';
+ ctx_sc = &context;
+ }
- lysc_update_path(ctx, NULL, NULL);
+ if (!identities_p) {
+ return LY_SUCCESS;
+ }
+ if (*identities) {
+ offset = LY_ARRAY_SIZE(*identities);
+ }
+
+ lysc_update_path(ctx_sc, NULL, "{identity}");
+ LY_ARRAY_CREATE_RET(ctx_sc->ctx, *identities, LY_ARRAY_SIZE(identities_p), LY_EMEM);
+ LY_ARRAY_FOR(identities_p, u) {
+ lysc_update_path(ctx_sc, NULL, identities_p[u].name);
+
+ LY_ARRAY_INCREMENT(*identities);
+ COMPILE_CHECK_UNIQUENESS_ARRAY(ctx_sc, *identities, name, &(*identities)[offset + u], "identity", identities_p[u].name);
+ DUP_STRING(ctx_sc->ctx, identities_p[u].name, (*identities)[offset + u].name);
+ DUP_STRING(ctx_sc->ctx, identities_p[u].dsc, (*identities)[offset + u].dsc);
+ DUP_STRING(ctx_sc->ctx, identities_p[u].ref, (*identities)[offset + u].ref);
+ (*identities)[offset + u].module = ctx_sc->mod;
+ COMPILE_ARRAY_GOTO(ctx_sc, identities_p[u].iffeatures, (*identities)[offset + u].iffeatures, v,
+ lys_compile_iffeature, ret, done);
+ /* backlinks (derived) can be added no sooner than when all the identities in the current module are present */
+ COMPILE_EXTS_GOTO(ctx_sc, identities_p[u].exts, (*identities)[offset + u].exts, &(*identities)[offset + u],
+ LYEXT_PAR_IDENT, ret, done);
+ (*identities)[offset + u].flags = identities_p[u].flags;
+
+ lysc_update_path(ctx_sc, NULL, NULL);
+ }
+ lysc_update_path(ctx_sc, NULL, NULL);
done:
return ret;
}
@@ -1044,17 +1060,18 @@
*
* @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
* @param[in] bases_p Array of names (including prefix if necessary) of base identities.
- * @param[in] ident Referencing identity to work with.
+ * @param[in] ident Referencing identity to work with, NULL for identityref.
* @param[in] bases Array of bases of identityref to fill in.
* @return LY_ERR value.
*/
static LY_ERR
-lys_compile_identity_bases(struct lysc_ctx *ctx, struct lys_module *context_module, const char **bases_p, struct lysc_ident *ident, struct lysc_ident ***bases)
+lys_compile_identity_bases(struct lysc_ctx *ctx, struct lys_module *context_module, const char **bases_p,
+ struct lysc_ident *ident, struct lysc_ident ***bases)
{
LY_ARRAY_SIZE_TYPE u, v;
const char *s, *name;
struct lys_module *mod;
- struct lysc_ident **idref;
+ struct lysc_ident **idref, *identities;
assert(ident || bases);
@@ -1064,7 +1081,7 @@
return LY_EVALID;
}
- for (u = 0; u < LY_ARRAY_SIZE(bases_p); ++u) {
+ LY_ARRAY_FOR(bases_p, u) {
s = strchr(bases_p[u], ':');
if (s) {
/* prefixed identity */
@@ -1084,27 +1101,31 @@
}
return LY_EVALID;
}
+
idref = NULL;
- if (mod->compiled && mod->compiled->identities) {
- for (v = 0; v < LY_ARRAY_SIZE(mod->compiled->identities); ++v) {
- if (!strcmp(name, mod->compiled->identities[v].name)) {
- if (ident) {
- if (ident == &mod->compiled->identities[v]) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Identity \"%s\" is derived from itself.", ident->name);
- return LY_EVALID;
- }
- LY_CHECK_RET(lys_compile_identity_circular_check(ctx, &mod->compiled->identities[v], ident->derived));
- /* we have match! store the backlink */
- LY_ARRAY_NEW_RET(ctx->ctx, mod->compiled->identities[v].derived, idref, LY_EMEM);
- *idref = ident;
- } else {
- /* we have match! store the found identity */
- LY_ARRAY_NEW_RET(ctx->ctx, *bases, idref, LY_EMEM);
- *idref = &mod->compiled->identities[v];
+ if (mod->compiled) {
+ identities = mod->compiled->identities;
+ } else {
+ identities = mod->dis_identities;
+ }
+ LY_ARRAY_FOR(identities, v) {
+ if (!strcmp(name, identities[v].name)) {
+ if (ident) {
+ if (ident == &identities[v]) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Identity \"%s\" is derived from itself.", ident->name);
+ return LY_EVALID;
}
- break;
+ LY_CHECK_RET(lys_compile_identity_circular_check(ctx, &identities[v], ident->derived));
+ /* we have match! store the backlink */
+ LY_ARRAY_NEW_RET(ctx->ctx, identities[v].derived, idref, LY_EMEM);
+ *idref = ident;
+ } else {
+ /* we have match! store the found identity */
+ LY_ARRAY_NEW_RET(ctx->ctx, *bases, idref, LY_EMEM);
+ *idref = &identities[v];
}
+ break;
}
}
if (!idref || !(*idref)) {
@@ -1133,6 +1154,7 @@
{
LY_ARRAY_SIZE_TYPE u;
+ lysc_update_path(ctx, NULL, "{identity}");
for (u = 0; u < LY_ARRAY_SIZE(idents_p); ++u) {
if (!idents_p[u].bases) {
continue;
@@ -1141,11 +1163,13 @@
LY_CHECK_RET(lys_compile_identity_bases(ctx, idents[u].module, idents_p[u].bases, &idents[u], NULL));
lysc_update_path(ctx, NULL, NULL);
}
+ lysc_update_path(ctx, NULL, NULL);
return LY_SUCCESS;
}
LY_ERR
-lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features)
+lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_feature *features_p, struct lysc_feature **features)
{
LY_ARRAY_SIZE_TYPE offset = 0, u;
struct lysc_ctx context = {0};
@@ -1305,35 +1329,35 @@
* @brief Revert compiled list of features back to the precompiled state.
*
* Function is needed in case the compilation failed and the schema is expected to revert back to the non-compiled status.
- * The features are supposed to be stored again as off_features in ::lys_module structure.
+ * The features are supposed to be stored again as dis_features in ::lys_module structure.
*
* @param[in] ctx Compilation context.
* @param[in] mod The module structure still holding the compiled (but possibly not finished, only the list of compiled features is taken) schema
- * and supposed to hold the off_features list.
+ * and supposed to hold the dis_features list.
*/
static void
lys_feature_precompile_revert(struct lysc_ctx *ctx, struct lys_module *mod)
{
LY_ARRAY_SIZE_TYPE u, v;
- /* keep the off_features list until the complete lys_module is freed */
- mod->off_features = mod->compiled->features;
+ /* keep the dis_features list until the complete lys_module is freed */
+ mod->dis_features = mod->compiled->features;
mod->compiled->features = NULL;
- /* in the off_features list, remove all the parts (from finished compiling process)
+ /* in the dis_features list, remove all the parts (from finished compiling process)
* which may points into the data being freed here */
- LY_ARRAY_FOR(mod->off_features, u) {
- LY_ARRAY_FOR(mod->off_features[u].iffeatures, v) {
- lysc_iffeature_free(ctx->ctx, &mod->off_features[u].iffeatures[v]);
+ LY_ARRAY_FOR(mod->dis_features, u) {
+ LY_ARRAY_FOR(mod->dis_features[u].iffeatures, v) {
+ lysc_iffeature_free(ctx->ctx, &mod->dis_features[u].iffeatures[v]);
}
- LY_ARRAY_FREE(mod->off_features[u].iffeatures);
- mod->off_features[u].iffeatures = NULL;
+ LY_ARRAY_FREE(mod->dis_features[u].iffeatures);
+ mod->dis_features[u].iffeatures = NULL;
- LY_ARRAY_FOR(mod->off_features[u].exts, v) {
- lysc_ext_instance_free(ctx->ctx, &(mod->off_features[u].exts)[v]);
+ LY_ARRAY_FOR(mod->dis_features[u].exts, v) {
+ lysc_ext_instance_free(ctx->ctx, &(mod->dis_features[u].exts)[v]);
}
- LY_ARRAY_FREE(mod->off_features[u].exts);
- mod->off_features[u].exts = NULL;
+ LY_ARRAY_FREE(mod->dis_features[u].exts);
+ mod->dis_features[u].exts = NULL;
}
}
@@ -6469,7 +6493,7 @@
struct lysc_module *mainmod = ctx->mod->compiled;
struct lysp_node *node_p;
- if (!mainmod->mod->off_features) {
+ if (!mainmod->mod->dis_features) {
/* features are compiled directly into the compiled module structure,
* but it must be done in two steps to allow forward references (via if-feature) between the features themselves.
* The features compilation is finished in the main module (lys_compile()). */
@@ -6477,9 +6501,10 @@
LY_CHECK_GOTO(ret, error);
}
- lysc_update_path(ctx, NULL, "{identity}");
- COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->identities, mainmod->identities, u, lys_compile_identity, ret, error);
- lysc_update_path(ctx, NULL, NULL);
+ if (!mainmod->mod->dis_identities) {
+ ret = lys_identity_precompile(ctx, NULL, NULL, submod->identities, &mainmod->identities);
+ LY_CHECK_GOTO(ret, error);
+ }
/* data nodes */
LY_LIST_FOR(submod->data, node_p) {
@@ -7078,16 +7103,17 @@
}
/* features */
- if ((*mod)->off_features) {
+ if ((*mod)->dis_features) {
/* there is already precompiled array of features */
- mod_c->features = (*mod)->off_features;
- (*mod)->off_features = NULL;
+ mod_c->features = (*mod)->dis_features;
+ (*mod)->dis_features = NULL;
} else {
/* features are compiled directly into the compiled module structure,
* but it must be done in two steps to allow forward references (via if-feature) between the features themselves */
ret = lys_feature_precompile(&ctx, NULL, NULL, sp->features, &mod_c->features);
LY_CHECK_GOTO(ret, error);
}
+
/* finish feature compilation, not only for the main module, but also for the submodules.
* Due to possible forward references, it must be done when all the features (including submodules)
* are present. */
@@ -7106,12 +7132,26 @@
}
lysc_update_path(&ctx, NULL, NULL);
- /* identities */
- lysc_update_path(&ctx, NULL, "{identity}");
- COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, u, lys_compile_identity, ret, error);
+ /* identities, work similarly to features with the precompilation */
+ if ((*mod)->dis_identities) {
+ mod_c->identities = (*mod)->dis_identities;
+ (*mod)->dis_identities = NULL;
+ } else {
+ ret = lys_identity_precompile(&ctx, NULL, NULL, sp->identities, &mod_c->identities);
+ LY_CHECK_GOTO(ret, error);
+ }
if (sp->identities) {
LY_CHECK_GOTO(ret = lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities), error);
}
+ lysc_update_path(&ctx, NULL, "{submodule}");
+ LY_ARRAY_FOR(sp->includes, v) {
+ if (sp->includes[v].submodule->identities) {
+ lysc_update_path(&ctx, NULL, sp->includes[v].name);
+ ret = lys_compile_identities_derived(&ctx, sp->includes[v].submodule->identities, mod_c->identities);
+ LY_CHECK_GOTO(ret, error);
+ lysc_update_path(&ctx, NULL, NULL);
+ }
+ }
lysc_update_path(&ctx, NULL, NULL);
/* data nodes */
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 58ac788..c931b4a 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -872,7 +872,8 @@
}
lysc_module_free(module->compiled, private_destructor);
- FREE_ARRAY(module->ctx, module->off_features, lysc_feature_free);
+ FREE_ARRAY(module->ctx, module->dis_features, lysc_feature_free);
+ FREE_ARRAY(module->ctx, module->dis_identities, lysc_ident_free);
lysp_module_free(module->parsed);
FREE_STRING(module->ctx, module->name);
@@ -887,6 +888,7 @@
free(module);
}
+
API void
lysc_extension_instance_free(struct ly_ctx *ctx, struct lysc_ext_substmt *substmts)
{
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index ed07a25..6dba9f2 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -588,6 +588,23 @@
struct lys_parser_ctx *main_ctx, const char *main_name, int required, void **result);
/**
+ * @brief Compile information from the identity statement
+ *
+ * The backlinks to the identities derived from this one are supposed to be filled later via lys_compile_identity_bases().
+ *
+ * @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p module.
+ * @param[in] ctx libyang context.
+ * @param[in] module Module of the features.
+ * @param[in] identities_p Array of the parsed identity definitions to precompile.
+ * @param[in,out] identities Pointer to the storage of the (pre)compiled identities array where the new identities are
+ * supposed to be added. The storage is supposed to be initiated to NULL when the first parsed identities are going
+ * to be processed.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_identity_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_ident *identities_p, struct lysc_ident **identities);
+
+/**
* @brief Create pre-compiled features array.
*
* Features are compiled in two steps to allow forward references between them via their if-feature statements.
diff --git a/tests/utests/schema/test_schema_stmts.c b/tests/utests/schema/test_schema_stmts.c
index a588e42..7e93afd 100644
--- a/tests/utests/schema/test_schema_stmts.c
+++ b/tests/utests/schema/test_schema_stmts.c
@@ -127,6 +127,21 @@
TEST_SCHEMA_ERR(ctx, 0, 0,"inv", "identity i1 {base i2;}identity i2 {base i3;}identity i3 {base i1;}",
"Identity \"i1\" is indirectly derived from itself. /inv:{identity='i3'}");
+ /* base in non-implemented module */
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb,
+ "module base {namespace \"urn\"; prefix b; identity i1; identity i2 {base i1;}}");
+ TEST_SCHEMA_OK(ctx, 0, 0, "ident", "import base {prefix b;} identity ii {base b:i1;}", mod);
+
+ /* default value from non-implemented module */
+ TEST_SCHEMA_ERR(ctx, 0, 0, "ident2", "import base {prefix b;} leaf l {type identityref {base b:i1;} default b:i2;}",
+ "Invalid leaf's default value \"b:i2\" which does not fit the type (Invalid identityref \"b:i2\" value"
+ " - identity found in non-implemented module \"base\".). /ident2:l");
+
+ /* default value in typedef from non-implemented module */
+ TEST_SCHEMA_ERR(ctx, 0, 0, "ident2", "import base {prefix b;} typedef t1 {type identityref {base b:i1;} default b:i2;}"
+ "leaf l {type t1;}", "Invalid type's default value \"b:i2\" which does not fit the type (Invalid"
+ " identityref \"b:i2\" value - identity found in non-implemented module \"base\".). /ident2:l");
+
/*
* printing
*/
diff --git a/tools/lint/commands.c b/tools/lint/commands.c
index a456e47..65200cf 100644
--- a/tools/lint/commands.c
+++ b/tools/lint/commands.c
@@ -1284,7 +1284,7 @@
if (module->compiled) {
features = module->compiled->features;
} else {
- features = module->off_features;
+ features = module->dis_features;
}
/* get the max len */