From a14125cf6968d126396fc19849b63c29e4f865f6 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 15 Nov 2022 23:30:15 +0100 Subject: [PATCH] Catch duplicate instantiation of statements The problem here is that during tryToReusePrototype() we are copying child statements, which in turn are triggering onStatementAdded(). For unique statements this incurs a SchemaTreeNamespace requirement on the target leaf -- which is satisified via requestSchemaTreeChild() and recorded in substatements (as partial materialization) and it is also recorded in the schema tree namespace. When this happens, though, effectiveCopy() does not notice the statement has already been copied and copies it again, and thus ends up colliding in the schema tree namespace -- pointing to the same statement as the source of the problem. Update effectiveCopy() to re-examing substatements to see if they reflect partial materialization and pick copied statement from there. JIRA: YANGTOOLS-1445 Change-Id: Ic184f872bf21e3e3c112b4fb5960fbe44262c77e Signed-off-by: Robert Varga --- .../reactor/InferredStatementContext.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java index b33191aefb..9271aa669c 100644 --- a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java +++ b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java @@ -41,6 +41,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.OnDemandSchemaTreeStorageNode; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType; import org.opendaylight.yangtools.yang.parser.spi.meta.StatementFactory; +import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference; @@ -574,9 +575,28 @@ final class InferredStatementContext, E extend .collect(Collectors.toUnmodifiableList()); } - private EffectiveCopy effectiveCopy(final ReactorStmtCtx stmt) { - final ReactorStmtCtx effective = stmt.asEffectiveChildOf(this, childCopyType(), targetModule); - return effective == null ? null : new EffectiveCopy(stmt, effective); + /** + * Create an effective copy of a prototype's substatement as a child of this statement. This is a bit tricky, as + * we are called from {@link #tryToReusePrototype(StatementFactory)} and we are creating copies of prototype + * statements -- which triggers {@link StatementSupport#onStatementAdded(Mutable)}, which in turn can loop around + * to {@link #requestSchemaTreeChild(QName)} -- which creates the statement and hence we can end up performing two + * copies. + * + * @param template Prototype substatement + * @return An {@link EffectiveCopy}, or {@code null} if not applicable + */ + private @Nullable EffectiveCopy effectiveCopy(final ReactorStmtCtx template) { + if (substatements instanceof HashMap) { + // we have partial materialization by requestSchemaTreeChild() after we started tryToReusePrototype(), check + // if the statement has already been copied -- we need to pick it up in that case. + final var copy = castMaterialized(substatements).get(template); + if (copy != null) { + return new EffectiveCopy(template, copy); + } + } + + final var copy = template.asEffectiveChildOf(this, childCopyType(), targetModule); + return copy == null ? null : new EffectiveCopy(template, copy); } private void copySubstatement(final Mutable substatement, final Collection> buffer, -- 2.36.6