schema compile CHANGE better handling of features
- allow forward reference in feature's if-feature statements
- handling features of not implemented modules - all such features
are permanently disabled, but the structures must be available for
the implemented (compiled) modules which import their module.
diff --git a/src/context.c b/src/context.c
index d95b7b6..ad3abb2 100644
--- a/src/context.c
+++ b/src/context.c
@@ -289,10 +289,10 @@
* module in the context.
* @return Module matching the given key, NULL if no such module found.
*/
-static const struct lys_module *
+static struct lys_module *
ly_ctx_get_module_by_iter(const struct ly_ctx *ctx, const char *key, size_t key_offset, unsigned int *index)
{
- const struct lys_module *mod;
+ struct lys_module *mod;
const char *value;
for (; *index < ctx->list.count; ++(*index)) {
@@ -317,21 +317,20 @@
* revision module, use ly_ctx_get_module_latest_by().
* @return Matching module if any.
*/
-static const struct lys_module *
+static struct lys_module *
ly_ctx_get_module_by(const struct ly_ctx *ctx, const char *key, size_t key_offset, const char *revision)
{
- const struct lys_module *mod;
+ struct lys_module *mod;
unsigned int index = 0;
while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
if (!revision) {
- if ((mod->compiled && !mod->compiled->revision) || (!mod->compiled && !mod->parsed->revs)) {
+ if (!mod->revision) {
/* found requested module without revision */
return mod;
}
} else {
- if ((mod->compiled && mod->compiled->revision && !strcmp(mod->compiled->revision, revision)) ||
- (!mod->compiled && mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, revision))) {
+ if (mod->revision && !strcmp(mod->revision, revision)) {
/* found requested module of the specific revision */
return mod;
}
@@ -341,14 +340,14 @@
return NULL;
}
-API const struct lys_module *
+API struct lys_module *
ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision)
{
LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
return ly_ctx_get_module_by(ctx, ns, offsetof(struct lys_module, ns), revision);
}
-API const struct lys_module *
+API struct lys_module *
ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision)
{
LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
@@ -362,10 +361,10 @@
* @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key.
* @return Matching module if any.
*/
-static const struct lys_module *
+static struct lys_module *
ly_ctx_get_module_latest_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
{
- const struct lys_module *mod;
+ struct lys_module *mod;
unsigned int index = 0;
while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
@@ -377,14 +376,14 @@
return NULL;
}
-API const struct lys_module *
+API struct lys_module *
ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name)
{
LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
return ly_ctx_get_module_latest_by(ctx, name, offsetof(struct lys_module, name));
}
-const struct lys_module *
+API struct lys_module *
ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns)
{
LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
@@ -398,10 +397,10 @@
* @param[in] key_offset Key's offset in struct lys_module to get value from the context's modules to match with the key.
* @return Matching module if any.
*/
-static const struct lys_module *
+static struct lys_module *
ly_ctx_get_module_implemented_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
{
- const struct lys_module *mod;
+ struct lys_module *mod;
unsigned int index = 0;
while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
@@ -413,14 +412,14 @@
return NULL;
}
-API const struct lys_module *
+API struct lys_module *
ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name)
{
LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
return ly_ctx_get_module_implemented_by(ctx, name, offsetof(struct lys_module, name));
}
-API const struct lys_module *
+API struct lys_module *
ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns)
{
LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
@@ -485,6 +484,40 @@
}
}
+API LY_ERR
+ly_ctx_module_implement(struct ly_ctx *ctx, struct lys_module *mod)
+{
+ struct lys_module *m;
+
+ LY_CHECK_ARG_RET(ctx, mod, LY_EINVAL);
+
+ if (mod->implemented) {
+ return LY_SUCCESS;
+ }
+
+ /* we have module from the current context */
+ m = ly_ctx_get_module_implemented(ctx, mod->name);
+ if (m) {
+ if (m != mod) {
+ /* check collision with other implemented revision */
+ LOGERR(ctx, LY_EDENIED, "Module \"%s\" is present in the context in other implemented revision (%s).",
+ mod->name, mod->revision ? mod->revision : "module without revision");
+ return LY_EDENIED;
+ } else {
+ /* mod is already implemented */
+ return LY_SUCCESS;
+ }
+ }
+
+ /* mark the module implemented, check for collision was already done */
+ mod->implemented = 1;
+
+ /* compile the schema */
+ LY_CHECK_RET(lys_compile(mod, 0));
+
+ return LY_SUCCESS;
+}
+
API void
ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv))
{
diff --git a/src/context.h b/src/context.h
index 8bed61b..e920cb0 100644
--- a/src/context.h
+++ b/src/context.h
@@ -63,6 +63,7 @@
directory, which is by default searched automatically (despite not
recursively). */
#define LY_CTX_PREFER_SEARCHDIRS 0x20 /**< When searching for schema, prefer searchdirs instead of user callback. */
+
/**@} contextoptions */
/**
@@ -206,7 +207,7 @@
* the schema with no revision is returned, if it is present in the context.
* @return Pointer to the YANG module, NULL if no schema in the context follows the name and revision requirements.
*/
-const struct lys_module *ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision);
+struct lys_module *ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision);
/**
* @brief Get the latest revision of the YANG module specified by its name.
@@ -218,7 +219,7 @@
* @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
* given name is present in the context.
*/
-const struct lys_module *ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name);
+struct lys_module *ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name);
/**
* @brief Get the (only) implemented YANG module specified by its name.
@@ -228,7 +229,7 @@
* @return The only implemented YANG module revision of the given name in the given context. NULL if there is no
* implemented module of the given name.
*/
-const struct lys_module *ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name);
+struct lys_module *ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name);
/**
* @brief Get YANG module of the given namespace and revision.
@@ -239,7 +240,7 @@
* the schema with no revision is returned, if it is present in the context.
* @return Pointer to the YANG module, NULL if no schema in the context follows the namespace and revision requirements.
*/
-const struct lys_module *ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision);
+struct lys_module *ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision);
/**
* @brief Get the latest revision of the YANG module specified by its namespace.
@@ -251,7 +252,7 @@
* @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
* given namespace is present in the context.
*/
-const struct lys_module *ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns);
+struct lys_module *ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns);
/**
* @brief Get the (only) implemented YANG module specified by its namespace.
@@ -261,7 +262,7 @@
* @return The only implemented YANG module revision of the given namespace in the given context. NULL if there is no
* implemented module of the given namespace.
*/
-const struct lys_module *ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns);
+struct lys_module *ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns);
/**
* @brief Reset cached latest revision information of the schemas in the context.
@@ -281,6 +282,17 @@
void ly_ctx_reset_latests(struct ly_ctx *ctx);
/**
+ * @brief Make the specific module implemented.
+ *
+ * @param[in] ctx libyang context to change.
+ * @param[in] mod Module from the given context to make implemented. It is not an error
+ * to provide already implemented module, it just does nothing.
+ * @return LY_SUCCESS or LY_EDENIED in case the context contains some other revision of the
+ * same module which is already implemented.
+ */
+LY_ERR ly_ctx_module_implement(struct ly_ctx *ctx, struct lys_module *mod);
+
+/**
* @brief Free all internal structures of the specified context.
*
* The function should be used before terminating the application to destroy
diff --git a/src/tree_schema.c b/src/tree_schema.c
index c3a4d2d..6fba4d6 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -241,24 +241,29 @@
* if-feature statements are again evaluated (disabled if a if-feature statemen
* evaluates to false).
*
- * @param[in] mod Compiled module where to set (search for) the feature.
+ * @param[in] mod Module where to set (search for) the feature.
* @param[in] name Name of the feature to set. Asterisk ('*') can be used to
* set all the features in the module.
* @param[in] value Desired value of the feature: 1 (enable) or 0 (disable).
* @return LY_ERR value.
*/
static LY_ERR
-lys_feature_change(const struct lysc_module *mod, const char *name, int value)
+lys_feature_change(const struct lys_module *mod, const char *name, int value)
{
int all = 0;
unsigned int u, changed_count, disabled_count;
struct lysc_feature *f, **df;
struct lysc_iffeature *iff;
struct ly_set *changed;
- struct ly_ctx *ctx = mod->mod->ctx; /* shortcut */
+ struct ly_ctx *ctx = mod->ctx; /* shortcut */
- if (!mod->features) {
- LOGERR(ctx, LY_EINVAL, "Unable to switch feature since the module \"%s\" has no features.", mod->mod->name);
+ if (!mod->compiled) {
+ LOGERR(ctx, LY_EINVAL, "Module \"%s\" is not implemented so all its features are permanently disabled without a chance to change it.",
+ mod->name);
+ return LY_EINVAL;
+ }
+ if (!mod->compiled->features) {
+ LOGERR(ctx, LY_EINVAL, "Unable to switch feature since the module \"%s\" has no features.", mod->name);
return LY_EINVAL;
}
@@ -270,8 +275,8 @@
changed_count = 0;
run:
- for (disabled_count = u = 0; u < LY_ARRAY_SIZE(mod->features); ++u) {
- f = &mod->features[u];
+ for (disabled_count = u = 0; u < LY_ARRAY_SIZE(mod->compiled->features); ++u) {
+ f = &mod->compiled->features[u];
if (all || !strcmp(f->name, name)) {
if ((value && (f->flags & LYS_FENABLED)) || (!value && !(f->flags & LYS_FENABLED))) {
if (all) {
@@ -320,7 +325,7 @@
}
if (!all && !changed->count) {
- LOGERR(ctx, LY_EINVAL, "Feature \"%s\" not found in module \"%s\".", name, mod->mod->name);
+ LOGERR(ctx, LY_EINVAL, "Feature \"%s\" not found in module \"%s\".", name, mod->name);
ly_set_free(changed, NULL);
return LY_EINVAL;
}
@@ -329,11 +334,11 @@
if (changed_count == changed->count) {
/* no change in last run -> not able to enable all ... */
/* ... print errors */
- for (u = 0; disabled_count && u < LY_ARRAY_SIZE(mod->features); ++u) {
- if (!(mod->features[u].flags & LYS_FENABLED)) {
+ for (u = 0; disabled_count && u < LY_ARRAY_SIZE(mod->compiled->features); ++u) {
+ if (!(mod->compiled->features[u].flags & LYS_FENABLED)) {
LOGERR(ctx, LY_EDENIED,
"Feature \"%s\" cannot be enabled since it is disabled by its if-feature condition(s).",
- mod->features[u].name);
+ mod->compiled->features[u].name);
--disabled_count;
}
}
@@ -384,17 +389,17 @@
API LY_ERR
lys_feature_enable(struct lys_module *module, const char *feature)
{
- LY_CHECK_ARG_RET(NULL, module, module->compiled, feature, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
- return lys_feature_change(module->compiled, feature, 1);
+ return lys_feature_change(module, feature, 1);
}
API LY_ERR
lys_feature_disable(struct lys_module *module, const char *feature)
{
- LY_CHECK_ARG_RET(NULL, module, module->compiled, feature, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
- return lys_feature_change(module->compiled, feature, 0);
+ return lys_feature_change(module, feature, 0);
}
API int
@@ -560,6 +565,9 @@
/* make sure that the newest revision is at position 0 */
lysp_sort_revisions(mod->parsed->revs);
+ if (mod->parsed->revs) {
+ mod->revision = lydict_insert(ctx, mod->parsed->revs[0].date, 0);
+ }
if (custom_check) {
LY_CHECK_GOTO(custom_check(ctx, mod->parsed, NULL, check_data), error);
@@ -575,7 +583,7 @@
}
/* check for duplicity in the context */
- mod_dup = (struct lys_module*)ly_ctx_get_module(ctx, mod->name, mod->parsed->revs ? mod->parsed->revs[0].date : NULL);
+ mod_dup = (struct lys_module*)ly_ctx_get_module(ctx, mod->name, mod->revision);
if (mod_dup) {
if (mod_dup->parsed) {
/* error */
@@ -612,15 +620,20 @@
}
#endif
+ if (!mod->implemented) {
+ /* pre-compile features of the module */
+ LY_CHECK_GOTO(lys_feature_precompile(ctx, mod->parsed->features, &mod->off_features), error);
+ }
+
/* decide the latest revision */
latest = (struct lys_module*)ly_ctx_get_module_latest(ctx, mod->name);
if (latest) {
- if (mod->parsed->revs) {
- if ((latest->parsed && !latest->parsed->revs) || (!latest->parsed && !latest->compiled->revision)) {
+ if (mod->revision) {
+ if (!latest->revision) {
/* latest has no revision, so mod is anyway newer */
mod->latest_revision = latest->latest_revision;
latest->latest_revision = 0;
- } else if (strcmp(mod->parsed->revs[0].date, latest->parsed ? latest->parsed->revs[0].date : latest->compiled->revision) > 0) {
+ } else if (strcmp(mod->revision, latest->revision) > 0) {
mod->latest_revision = latest->latest_revision;
latest->latest_revision = 0;
}
@@ -653,6 +666,10 @@
if (!inc->submodule && lysp_load_submodule(&context, mod->parsed, inc)) {
goto error_ctx;
}
+ if (!mod->implemented) {
+ /* pre-compile features of the module */
+ LY_CHECK_GOTO(lys_feature_precompile(ctx, inc->submodule->features, &mod->off_features), error);
+ }
}
mod->parsed->parsing = 0;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 3301d74..bcf92d4 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1394,7 +1394,6 @@
*/
struct lysc_module {
struct lys_module *mod; /**< covering module structure */
- const char *revision; /**< the revision of the module */
struct lysc_import *imports; /**< list of imported modules ([sized array](@ref sizedarrays)) */
struct lysc_feature *features; /**< list of feature definitions ([sized array](@ref sizedarrays)) */
@@ -1494,6 +1493,7 @@
struct lys_module {
struct ly_ctx *ctx; /**< libyang context of the module (mandatory) */
const char *name; /**< name of the module (mandatory) */
+ const char *revision; /**< revision of the module (if present) */
const char *ns; /**< namespace of the module (module - mandatory) */
const char *prefix; /**< module prefix or submodule belongsto prefix of main module (mandatory) */
const char *filepath; /**< path, if the schema was read from a file, NULL in case of reading from memory */
@@ -1503,7 +1503,14 @@
const char *ref; /**< cross-reference for the module */
struct lysp_module *parsed; /**< Simply parsed (unresolved) YANG schema tree */
- struct lysc_module *compiled; /**< Compiled and fully validated YANG schema tree for data parsing */
+ 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)).
+ 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
+ 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()). */
uint8_t implemented:1; /**< flag if the module is implemented, not just imported */
uint8_t latest_revision:2; /**< flag to mark the latest available revision:
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index ef6fc25..f94cfed 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -62,8 +62,8 @@
#define COMPILE_CHECK_UNIQUENESS(CTX, ARRAY, MEMBER, EXCL, STMT, IDENT) \
if (ARRAY) { \
- for (unsigned int u = 0; u < LY_ARRAY_SIZE(ARRAY); ++u) { \
- if (&(ARRAY)[u] != EXCL && (void*)((ARRAY)[u].MEMBER) == (void*)(IDENT)) { \
+ for (unsigned int u__ = 0; u__ < LY_ARRAY_SIZE(ARRAY); ++u__) { \
+ if (&(ARRAY)[u__] != EXCL && (void*)((ARRAY)[u__].MEMBER) == (void*)(IDENT)) { \
LOGVAL((CTX)->ctx, LY_VLOG_STR, (CTX)->path, LY_VCODE_DUPIDENT, IDENT, STMT); \
return LY_EVALID; \
} \
@@ -174,16 +174,31 @@
#define LYS_IFF_LP 0x04 /* ( */
#define LYS_IFF_RP 0x08 /* ) */
+/**
+ * @brief Find a feature of the given name and referenced in the given module.
+ *
+ * If the compiled schema is available (the schema is implemented), the feature from the compiled schema is
+ * returned. Otherwise, the special array of pre-compiled features is used to search for the feature. Such
+ * features are always disabled (feature from not implemented schema cannot be enabled), but in case the schema
+ * will be made implemented in future (no matter if implicitly via augmenting/deviating it or explicitly via
+ * ly_ctx_module_implement()), the compilation of these feature structure is finished, but the pointers
+ * assigned till that time will be still valid.
+ *
+ * @param[in] mod Module where the feature was referenced (used to resolve prefix of the feature).
+ * @param[in] name Name of the feature including possible prefix.
+ * @param[in] len Length of the string representing the feature identifier in the name variable (mandatory!).
+ * @return Pointer to the feature structure if found, NULL otherwise.
+ */
static struct lysc_feature *
-lysc_feature_find(struct lysc_module *mod, const char *name, size_t len)
+lys_feature_find(struct lys_module *mod, const char *name, size_t len)
{
size_t i;
- struct lysc_feature *f;
+ struct lysc_feature *f, *flist;
for (i = 0; i < len; ++i) {
if (name[i] == ':') {
/* we have a prefixed feature */
- mod = lysc_module_find_prefix(mod, name, i);
+ mod = lys_module_find_prefix(mod, name, i);
LY_CHECK_RET(!mod, NULL);
name = &name[i + 1];
@@ -192,8 +207,14 @@
}
/* we have the correct module, get the feature */
- LY_ARRAY_FOR(mod->features, i) {
- f = &mod->features[i];
+ if (mod->implemented) {
+ /* module is implemented so there is already the compiled schema */
+ flist = mod->compiled->features;
+ } else {
+ flist = mod->off_features;
+ }
+ LY_ARRAY_FOR(flist, i) {
+ f = &flist[i];
if (!strncmp(f->name, name, len) && f->name[len] == '\0') {
return f;
}
@@ -382,18 +403,10 @@
iff_setop(iff->expr, LYS_IFF_F, expr_size--);
/* now get the link to the feature definition */
- if (!ctx->mod_def->compiled) {
- /* TODO - permanently switched off feature - link to some static feature and update when the schema gets implemented */
- LOGINT(ctx->ctx);
- goto error;
- } else {
- f = lysc_feature_find(ctx->mod_def->compiled, &c[i], j - i);
- }
- LY_CHECK_ERR_GOTO(!f,
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", *value, j - i, &c[i]);
- rc = LY_EVALID,
- error)
+ f = lys_feature_find(ctx->mod_def, &c[i], j - i);
+ LY_CHECK_ERR_GOTO(!f, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", *value, j - i, &c[i]);
+ rc = LY_EVALID, error)
iff->features[f_size] = f;
LY_ARRAY_INCREMENT(iff->features);
f_size--;
@@ -481,7 +494,7 @@
}
}
if (!mod) {
- if (lysp_load_module(ctx->ctx, imp->module->name, imp->module->compiled->revision, 0, 1, &mod)) {
+ if (lysp_load_module(ctx->ctx, imp->module->name, imp->module->revision, 0, 1, &mod)) {
LOGERR(ctx->ctx, LY_ENOTFOUND, "Unable to reload \"%s\" module to import it into \"%s\", source data not found.",
imp->module->name, ctx->mod->name);
return LY_ENOTFOUND;
@@ -613,44 +626,81 @@
return LY_SUCCESS;
}
+LY_ERR
+lys_feature_precompile(struct ly_ctx *ctx, struct lysp_feature *features_p, struct lysc_feature **features)
+{
+ unsigned int offset = 0, u;
+ struct lysc_ctx context = {0};
+
+ assert(ctx);
+ context.ctx = ctx;
+
+ if (!features_p) {
+ return LY_SUCCESS;
+ }
+ if (*features) {
+ offset = LY_ARRAY_SIZE(*features);
+ }
+
+ LY_ARRAY_CREATE_RET(ctx, *features, LY_ARRAY_SIZE(features_p), LY_EMEM);
+ LY_ARRAY_FOR(features_p, u) {
+ LY_ARRAY_INCREMENT(*features);
+ COMPILE_CHECK_UNIQUENESS(&context, *features, name, &(*features)[offset + u], "feature", features_p[u].name);
+ DUP_STRING(ctx, features_p[u].name, (*features)[offset + u].name);
+ DUP_STRING(ctx, features_p[u].dsc, (*features)[offset + u].dsc);
+ DUP_STRING(ctx, features_p[u].ref, (*features)[offset + u].ref);
+ (*features)[offset + u].flags = features_p[u].flags;
+ }
+
+ return LY_SUCCESS;
+}
+
/**
- * @brief Create compiled feature structure.
+ * @brief Create pre-compiled features array.
+ *
+ * See lys_feature_precompile() for more details.
+ *
* @param[in] ctx Compile context.
* @param[in] feature_p Parsed feature definition to compile.
* @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
- * @param[in] features List of already compiled features to check name duplicity.
- * @param[in,out] feature Compiled feature structure to fill.
+ * @param[in,out] features List of already (pre)compiled features to find the corresponding precompiled feature structure.
* @return LY_ERR value.
*/
static LY_ERR
-lys_compile_feature(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *features, struct lysc_feature *feature)
+lys_feature_precompile_finish(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *features)
{
- unsigned int u, v;
+ unsigned int u, v, x;
+ struct lysc_feature *feature, **df;
LY_ERR ret = LY_SUCCESS;
- struct lysc_feature **df;
- COMPILE_CHECK_UNIQUENESS(ctx, features, name, feature, "feature", feature_p->name);
- DUP_STRING(ctx->ctx, feature_p->name, feature->name);
- DUP_STRING(ctx->ctx, feature_p->dsc, feature->dsc);
- DUP_STRING(ctx->ctx, feature_p->ref, feature->ref);
- feature->flags = feature_p->flags;
+ /* find the preprecompiled feature */
+ LY_ARRAY_FOR(features, x) {
+ if (strcmp(features[x].name, feature_p->name)) {
+ continue;
+ }
+ feature = &features[x];
- COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, options, u, lys_compile_ext, ret, done);
- COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, options, u, lys_compile_iffeature, ret, done);
- if (feature->iffeatures) {
- for (u = 0; u < LY_ARRAY_SIZE(feature->iffeatures); ++u) {
- if (feature->iffeatures[u].features) {
- for (v = 0; v < LY_ARRAY_SIZE(feature->iffeatures[u].features); ++v) {
- /* add itself into the dependants list */
- LY_ARRAY_NEW_RET(ctx->ctx, feature->iffeatures[u].features[v]->depfeatures, df, LY_EMEM);
- *df = feature;
+ /* finish compilation started in lys_feature_precompile() */
+ COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, options, u, lys_compile_ext, ret, done);
+ COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, options, u, lys_compile_iffeature, ret, done);
+ if (feature->iffeatures) {
+ for (u = 0; u < LY_ARRAY_SIZE(feature->iffeatures); ++u) {
+ if (feature->iffeatures[u].features) {
+ for (v = 0; v < LY_ARRAY_SIZE(feature->iffeatures[u].features); ++v) {
+ /* add itself into the dependants list */
+ LY_ARRAY_NEW_RET(ctx->ctx, feature->iffeatures[u].features[v]->depfeatures, df, LY_EMEM);
+ *df = feature;
+ }
+ /* TODO check for circular dependency */
}
- /* TODO check for circular dependency */
}
}
+ done:
+ return ret;
}
-done:
- return ret;
+
+ LOGINT(ctx->ctx);
+ return LY_EINT;
}
/**
@@ -3857,7 +3907,15 @@
struct lysp_submodule *submod = inc->submodule;
struct lysc_module *mainmod = ctx->mod->compiled;
- COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->features, mainmod->features, options, u, lys_compile_feature, ret, error);
+ if (!mainmod->mod->off_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()). */
+ ret = lys_feature_precompile(ctx->ctx, submod->features,
+ mainmod->mod->off_features ? &mainmod->mod->off_features : &mainmod->features);
+ LY_CHECK_GOTO(ret, error);
+ }
+
COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->identities, mainmod->identities, options, u, lys_compile_identity, ret, error);
error:
@@ -3873,6 +3931,7 @@
struct lysp_module *sp;
struct lysp_node *node_p;
unsigned int u, v;
+ int using_precompiled_features = 0;
LY_ERR ret = LY_SUCCESS;
LY_CHECK_ARG_RET(NULL, mod, mod->parsed, mod->ctx, LY_EINVAL);
@@ -3892,15 +3951,36 @@
LY_CHECK_ERR_RET(!mod_c, LOGMEM(mod->ctx), LY_EMEM);
mod_c->mod = mod;
- if (sp->revs) {
- DUP_STRING(mod->ctx, sp->revs[0].date, mod_c->revision);
- }
COMPILE_ARRAY_GOTO(&ctx, sp->imports, mod_c->imports, options, u, lys_compile_import, ret, error);
LY_ARRAY_FOR(sp->includes, u) {
ret = lys_compile_submodule(&ctx, &sp->includes[u], options);
LY_CHECK_GOTO(ret != LY_SUCCESS, error);
}
- COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->features, mod_c->features, options, u, lys_compile_feature, ret, error);
+ if (mod->off_features) {
+ /* there is already precompiled array of features */
+ mod_c->features = mod->off_features;
+ mod->off_features = NULL;
+ using_precompiled_features = 1;
+ } 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.ctx, 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. */
+ LY_ARRAY_FOR(sp->features, u) {
+ ret = lys_feature_precompile_finish(&ctx, &sp->features[u], options, mod_c->features);
+ LY_CHECK_GOTO(ret != LY_SUCCESS, error);
+ }
+ LY_ARRAY_FOR(sp->includes, v) {
+ LY_ARRAY_FOR(sp->includes[v].submodule->features, u) {
+ ret = lys_feature_precompile_finish(&ctx, &sp->includes[v].submodule->features[u], options, mod_c->features);
+ LY_CHECK_GOTO(ret != LY_SUCCESS, error);
+ }
+ }
+
COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, options, u, lys_compile_identity, ret, error);
if (sp->identities) {
LY_CHECK_RET(lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities));
@@ -3972,6 +4052,23 @@
return LY_SUCCESS;
error:
+ if (using_precompiled_features) {
+ /* keep the off_features list until the complete lys_module is freed */
+ mod->off_features = mod->compiled->features;
+ mod->compiled->features = NULL;
+ }
+ /* in the off_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_FREE(mod->off_features[u].iffeatures);
+ LY_ARRAY_FOR(mod->off_features[u].exts, v) {
+ lysc_ext_instance_free(ctx.ctx, &(mod->off_features[u].exts)[v]);
+ }
+ LY_ARRAY_FREE(mod->off_features[u].exts);
+ }
ly_set_erase(&ctx.unres, NULL);
ly_set_erase(&ctx.groupings, NULL);
lysc_module_free(mod_c, NULL);
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index fa3fff9..ce0dfb8 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -464,14 +464,14 @@
free(module);
}
-static void
+void
lysc_ext_instance_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext)
{
FREE_STRING(ctx, ext->argument);
FREE_ARRAY(ctx, ext->exts, lysc_ext_instance_free);
}
-static void
+void
lysc_iffeature_free(struct ly_ctx *UNUSED(ctx), struct lysc_iffeature *iff)
{
LY_ARRAY_FREE(iff->features);
@@ -757,8 +757,6 @@
LY_CHECK_ARG_RET(NULL, module,);
ctx = module->mod->ctx;
- FREE_STRING(ctx, module->revision);
-
FREE_ARRAY(ctx, module->imports, lysc_import_free);
FREE_ARRAY(ctx, module->features, lysc_feature_free);
FREE_ARRAY(ctx, module->identities, lysc_ident_free);
@@ -788,9 +786,11 @@
}
lysc_module_free(module->compiled, private_destructor);
+ FREE_ARRAY(module->ctx, module->off_features, lysc_feature_free);
lysp_module_free(module->parsed);
FREE_STRING(module->ctx, module->name);
+ FREE_STRING(module->ctx, module->revision);
FREE_STRING(module->ctx, module->ns);
FREE_STRING(module->ctx, module->prefix);
FREE_STRING(module->ctx, module->filepath);
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 692acaa..20a06dc 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -681,12 +681,17 @@
LYS_INFORMAT format = LYS_IN_UNKNOWN;
void (*module_data_free)(void *module_data, void *user_data) = NULL;
struct lysp_load_module_check_data check_data = {0};
+ struct lys_module *m;
- /* try to get the module from the context */
- if (revision) {
- *mod = (struct lys_module*)ly_ctx_get_module(ctx, name, revision);
- } else {
- *mod = (struct lys_module*)ly_ctx_get_module_latest(ctx, name);
+ assert(mod);
+
+ if (!*mod) {
+ /* try to get the module from the context */
+ if (revision) {
+ *mod = (struct lys_module*)ly_ctx_get_module(ctx, name, revision);
+ } else {
+ *mod = (struct lys_module*)ly_ctx_get_module_latest(ctx, name);
+ }
}
if (!(*mod) || (require_parsed && !(*mod)->parsed)) {
@@ -740,12 +745,15 @@
}
} else {
/* we have module from the current context */
- if (implement && (ly_ctx_get_module_implemented(ctx, name) != *mod)) {
- /* check collision with other implemented revision */
- LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
- "Module \"%s\" is already present in other implemented revision.", name);
- *mod = NULL;
- return LY_EDENIED;
+ if (implement) {
+ m = ly_ctx_get_module_implemented(ctx, name);
+ if (m && m != *mod) {
+ /* check collision with other implemented revision */
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
+ "Module \"%s\" is already present in other implemented revision.", name);
+ *mod = NULL;
+ return LY_EDENIED;
+ }
}
/* circular check */
@@ -828,7 +836,7 @@
TYPE *imp; \
if (!strncmp((MOD)->mod->prefix, prefix, len) && (MOD)->mod->prefix[len] == '\0') { \
/* it is the prefix of the module itself */ \
- m = ly_ctx_get_module((MOD)->mod->ctx, (MOD)->mod->name, ((struct lysc_module*)(MOD))->revision); \
+ m = ly_ctx_get_module((MOD)->mod->ctx, (MOD)->mod->name, (MOD)->mod->revision); \
} \
/* search in imports */ \
if (!m) { \
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index d1adfa5..b30c123 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -61,12 +61,12 @@
struct lysc_ctx {
struct ly_ctx *ctx;
struct lys_module *mod;
- struct lys_module *mod_def; /* context module for the definitions of the nodes being currently
- processed - groupings are supposed to be evaluated in place where
- defined, but its content instances are supposed to be placed into
- the target module (mod) */
- struct ly_set groupings; /* stack for groupings circular check */
- struct ly_set unres; /* to validate leafref's target and xpath of when/must */
+ struct lys_module *mod_def; /**< context module for the definitions of the nodes being currently
+ processed - groupings are supposed to be evaluated in place where
+ defined, but its content instances are supposed to be placed into
+ the target module (mod) */
+ struct ly_set groupings; /**< stack for groupings circular check */
+ struct ly_set unres; /**< to validate leafref's target and xpath of when/must */
uint16_t path_len;
#define LYSC_CTX_BUFSIZE 4078
char path[LYSC_CTX_BUFSIZE];
@@ -436,6 +436,27 @@
void **result);
/**
+ * @brief Create pre-compiled features array.
+ *
+ * Features are compiled in two steps to allow forward references between them via their if-feature statements.
+ * In case of not implemented schemas, the precompiled list of features is stored in lys_module structure and
+ * the compilation is not finished (if-feature and extensions are missing) and all the features are permanently
+ * disabled without a chance to change it. The list is used as target for any if-feature statement in any
+ * implemented module to get valid data to evaluate its result. The compilation is finished via
+ * lys_feature_precompile_finish() in implemented modules. In case a not implemented module becomes implemented,
+ * the precompiled list is reused to finish the compilation to preserve pointers already used in various compiled
+ * if-feature structures.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] features_p Array if the parsed features definitions to precompile.
+ * @param[in,out] features Pointer to the storage of the (pre)compiled features array where the new features are
+ * supposed to be added. The storage is supposed to be initiated to NULL when the first parsed features are going
+ * to be processed.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_feature_precompile(struct ly_ctx *ctx, struct lysp_feature *features_p, struct lysc_feature **features);
+
+/**
* @brief Free the parsed submodule structure.
* @param[in] ctx libyang context where the string data resides in a dictionary.
* @param[in,out] submod Parsed schema submodule structure to free.
@@ -450,6 +471,22 @@
void lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type);
/**
+ * @brief Free the compiled if-feature structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] iff Compiled if-feature structure to be cleaned.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+void lysc_iffeature_free(struct ly_ctx *ctx, struct lysc_iffeature *iff);
+
+/**
+ * @brief Free the compiled extension instance structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] ext Compiled extension instance structure to be cleaned.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+void lysc_ext_instance_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext);
+
+/**
* @brief Free the compiled node structure.
* @param[in] ctx libyang context where the string data resides in a dictionary.
* @param[in,out] node Compiled node structure to be freed.
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index b3e4917..1288ee3 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -286,6 +286,22 @@
assert_int_equal(1, lys_feature_value(&mod, "f1"));
assert_int_equal(0, lys_feature_value(&mod, "f2"));
+ assert_non_null(modp = lys_parse_mem(ctx.ctx, "module b {namespace urn:b;prefix b;"
+ "feature f1 {if-feature f2;}feature f2;}", LYS_IN_YANG));
+ assert_non_null(modp->compiled);
+ assert_non_null(modp->compiled->features);
+ assert_int_equal(2, LY_ARRAY_SIZE(modp->compiled->features));
+ assert_non_null(modp->compiled->features[0].iffeatures);
+ assert_int_equal(1, LY_ARRAY_SIZE(modp->compiled->features[0].iffeatures));
+ assert_non_null(modp->compiled->features[0].iffeatures[0].features);
+ assert_int_equal(1, LY_ARRAY_SIZE(modp->compiled->features[0].iffeatures[0].features));
+ assert_ptr_equal(&modp->compiled->features[1], modp->compiled->features[0].iffeatures[0].features[0]);
+ assert_non_null(modp->compiled->features);
+ assert_int_equal(2, LY_ARRAY_SIZE(modp->compiled->features));
+ assert_non_null(modp->compiled->features[1].depfeatures);
+ assert_int_equal(1, LY_ARRAY_SIZE(modp->compiled->features[1].depfeatures));
+ assert_ptr_equal(&modp->compiled->features[0], modp->compiled->features[1].depfeatures[0]);
+
/* invalid reference */
assert_int_equal(LY_EINVAL, lys_feature_enable(&mod, "xxx"));
logbuf_assert("Feature \"xxx\" not found in module \"a\".");
@@ -333,8 +349,8 @@
logbuf_assert("Duplicate identifier \"f1\" of feature statement.");
reset_mod(ctx.ctx, &mod);
- ly_ctx_set_module_imp_clb(ctx.ctx, test_imp_clb, "submodule sb {belongs-to b {prefix b;} feature f1;}");
- assert_null(lys_parse_mem(ctx.ctx, "module b{namespace urn:b; prefix b; include sb;feature f1;}", LYS_IN_YANG));
+ ly_ctx_set_module_imp_clb(ctx.ctx, test_imp_clb, "submodule sz {belongs-to z {prefix z;} feature f1;}");
+ assert_null(lys_parse_mem(ctx.ctx, "module z{namespace urn:z; prefix z; include sz;feature f1;}", LYS_IN_YANG));
logbuf_assert("Duplicate identifier \"f1\" of feature statement.");
/* import reference */
@@ -2090,10 +2106,8 @@
assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
- assert_non_null(lys_parse_mem(ctx, "module grp {namespace urn:grp;prefix g; typedef mytype {type string;}"
- "grouping grp {leaf x {type mytype;}}}", LYS_IN_YANG));
-
-
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "module grp {namespace urn:grp;prefix g; typedef mytype {type string;} feature f;"
+ "grouping grp {leaf x {type mytype;} leaf y {type string; if-feature f;}}}");
assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;import grp {prefix g;}"
"grouping grp_a_top {leaf a1 {type int8;}}"
"container a {uses grp_a; uses grp_a_top; uses g:grp; grouping grp_a {leaf a2 {type uint8;}}}}", LYS_IN_YANG));
@@ -2108,6 +2122,22 @@
assert_non_null((child = child->next));
assert_string_equal("x", child->name);
assert_ptr_equal(mod, child->module);
+ assert_non_null((child = child->next));
+ assert_string_equal("y", child->name);
+ assert_non_null(child->iffeatures);
+ assert_int_equal(1, LY_ARRAY_SIZE(child->iffeatures));
+ assert_int_equal(1, LY_ARRAY_SIZE(child->iffeatures[0].features));
+ assert_string_equal("f", child->iffeatures[0].features[0]->name);
+ assert_int_equal(LY_EINVAL, lys_feature_enable(mod->compiled->imports[0].module, "f"));
+ logbuf_assert("Module \"grp\" is not implemented so all its features are permanently disabled without a chance to change it.");
+ assert_int_equal(0, lysc_iffeature_value(&child->iffeatures[0]));
+
+ /* make the imported module implemented and enable the feature */
+ assert_non_null(mod = ly_ctx_get_module(ctx, "grp", NULL));
+ assert_int_equal(LY_SUCCESS, ly_ctx_module_implement(ctx, mod));
+ assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "f"));
+ assert_string_equal("f", child->iffeatures[0].features[0]->name);
+ assert_int_equal(1, lysc_iffeature_value(&child->iffeatures[0]));
ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule bsub {belongs-to b {prefix b;} grouping grp {leaf b {type string;}}}");
assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;include bsub;uses grp;}", LYS_IN_YANG));