Catch duplicate instantiation of statements 78/103278/1
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 15 Nov 2022 22:30:15 +0000 (23:30 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 16 Nov 2022 19:00:19 +0000 (20:00 +0100)
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 <robert.varga@pantheon.tech>
(cherry picked from commit a14125cf6968d126396fc19849b63c29e4f865f6)

parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java

index 48af7eb860e2673b8e56ab96a500442906a467b5..144d6b5c03bd22ebe8ce72879baa0d45befb40e6 100644 (file)
@@ -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;
@@ -571,9 +572,28 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, 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<ReactorStmtCtx<?, ?, ?>> buffer,