Catch duplicate instantiation of statements 50/102550/7
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 15 Nov 2022 22:30:15 +0000 (23:30 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 15 Nov 2022 22:33:04 +0000 (23:33 +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>
parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java

index b33191aefb601234e66265a7929b1b18d2f04c52..9271aa669c4efc525186efb6a9fc41535eb2a899 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;
@@ -574,9 +575,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,