Defer substatement initialization in InferredStatementContext 27/93527/2
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 22 Aug 2020 18:27:23 +0000 (20:27 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 31 Oct 2020 08:04:48 +0000 (09:04 +0100)
InferredStatementContext is initializing its substatements rather
eagerly in its constructor. We now have all the tools we need to
defer this instantiation until the substatements are really needed.

JIRA: YANGTOOLS-784
Change-Id: Iafc23c22b15efdf46fde3adfc210c246e8d086bb
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit 3f3df266fb8ba0b06ffb6492766e8f49b167b05d)

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

index fb153a83c8381d130e2ebca87e8cd8471c7fb809..08b632cb808c05f0691bebdaaf14fc65f6b29926 100644 (file)
@@ -17,14 +17,17 @@ import java.util.List;
 import java.util.Optional;
 import java.util.stream.Stream;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+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.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
@@ -37,7 +40,7 @@ import org.slf4j.LoggerFactory;
  * effective substatements, which are either transformed from that prototype or added by inference.
  */
 final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
-        extends StatementContextBase<A, D, E> {
+        extends StatementContextBase<A, D, E> implements OnDemandSchemaTreeStorageNode {
     private static final Logger LOG = LoggerFactory.getLogger(InferredStatementContext.class);
 
     private final @NonNull StatementContextBase<A, D, E> prototype;
@@ -56,6 +59,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         this.prototype = original.prototype;
         this.originalCtx = original.originalCtx;
         this.argument = original.argument;
+        setSubstatementsInitialized();
     }
 
     InferredStatementContext(final StatementContextBase<?, ?, ?> parent, final StatementContextBase<A, D, E> prototype,
@@ -69,8 +73,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         this.targetModule = targetModule;
         this.originalCtx = prototype.getOriginalCtx().orElse(prototype);
 
-        // FIXME: YANGTOOLS-784: instantiate these lazily
-        addEffectiveSubstatements(createEffective());
+        // Note: substatements from prototype are initialized lazily through ensureSubstatements()
     }
 
     @Override
@@ -119,6 +122,37 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         return originalCtx.buildDeclared();
     }
 
+    @Override
+    public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
+        ensureSubstatements();
+        return super.mutableEffectiveSubstatements();
+    }
+
+    @Override
+    public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
+        ensureSubstatements();
+        super.addEffectiveSubstatement(substatement);
+    }
+
+    @Override
+    public void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
+        ensureSubstatements();
+        super.addEffectiveSubstatements(statements);
+    }
+
+    @Override
+    public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
+            final String statementArg) {
+        ensureSubstatements();
+        super.removeStatementFromEffectiveSubstatements(statementDef, statementArg);
+    }
+
+    @Override
+    public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
+        ensureSubstatements();
+        super.removeStatementFromEffectiveSubstatements(statementDef);
+    }
+
     @Override
     InferredStatementContext<A, D, E> reparent(final StatementContextBase<?, ?, ?> newParent) {
         return new InferredStatementContext<>(this, newParent);
@@ -126,12 +160,37 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
 
     @Override
     boolean hasEmptySubstatements() {
+        ensureSubstatements();
         return hasEmptyEffectiveSubstatements();
     }
 
+    @Override
+    public <D extends DeclaredStatement<QName>, E extends EffectiveStatement<QName, D>>
+            StmtContext<QName, D, E> requestSchemaTreeChild(final QName qname) {
+        LOG.debug("Materializing on lookup of {}", qname);
+        // FIXME: YANGTOOLS-1160: we do not want to force full materialization here
+        ensureSubstatements();
+
+        // Now we have to do a lookup as we do not have access to the namespace being populated (yet). Here we are
+        // bypassing additional checks and talk directly to superclass to get the statements.
+        for (StmtContext<?, ?, ?> stmt : super.mutableEffectiveSubstatements()) {
+            if (stmt.producesEffective(SchemaTreeEffectiveStatement.class)
+                && qname.equals(stmt.coerceStatementArgument())) {
+                return (StmtContext<QName, D, E>) stmt;
+            }
+        }
+        return null;
+    }
+
     // Instantiate this statement's effective substatements. Note this method has side-effects in namespaces and overall
     // BuildGlobalContext, hence it must be called at most once.
-    private List<Mutable<?, ?, ?>> createEffective() {
+    private void ensureSubstatements() {
+        if (!substatementsInitialized()) {
+            initializeSubstatements();
+        }
+    }
+
+    private void initializeSubstatements() {
         final Collection<? extends StatementContextBase<?, ?, ?>> declared = prototype.mutableDeclaredSubstatements();
         final Collection<? extends Mutable<?, ?, ?>> effective = prototype.mutableEffectiveSubstatements();
         final List<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
@@ -145,7 +204,9 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
             copySubstatement(stmtContext, buffer);
         }
 
-        return buffer;
+        // We are bypassing usual safeties here, as this is not introducing new statements but rather just materializing
+        // them when the need has arised.
+        addInitialEffectiveSubstatements(buffer);
     }
 
     // Statement copy mess starts here
index 3d1f8f97b31a4d4bfebfe8c8880b6784104ae4c8..3060eb7916f23e9a3a3e880fb80dd61fa83207ca 100644 (file)
@@ -69,7 +69,6 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
 import org.opendaylight.yangtools.yang.parser.spi.source.ImplicitSubstatement;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
-import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
 import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.KeyedValueAddedListener;
@@ -155,6 +154,9 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     // Flag for use with AbstractResumedStatement. This is hiding in the alignment shadow created by above boolean
     private boolean fullyDefined;
 
+    // Flag for InferredStatementContext. This is hiding in the alignment shadow created by above boolean.
+    private boolean substatementsInitialized;
+
     // Flags for use with SubstatementContext. These are hiding in the alignment shadow created by above boolean and
     // hence improve memory layout.
     private byte flags;
@@ -479,20 +481,35 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         if (!statements.isEmpty()) {
             statements.forEach(StatementContextBase::verifyStatement);
             beforeAddEffectiveStatement(statements.size());
+            doAddEffectiveSubstatements(statements);
+        }
+    }
 
-            final Collection<? extends StatementContextBase<?, ?, ?>> casted =
-                    (Collection<? extends StatementContextBase<?, ?, ?>>) statements;
-            final ModelProcessingPhase phase = completedPhase;
-            if (phase != null) {
-                for (StatementContextBase<?, ?, ?> stmt : casted) {
-                    ensureCompletedPhase(stmt, phase);
-                }
-            }
+    // exposed for InferredStatementContext only
+    final void addInitialEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
+        verify(!substatementsInitialized, "Attempted to re-initialized statement {} with {}", this, statements);
+        substatementsInitialized = true;
 
-            effective.addAll(casted);
+        if (!statements.isEmpty()) {
+            statements.forEach(StatementContextBase::verifyStatement);
+            beforeAddEffectiveStatementUnsafe(statements.size());
+            doAddEffectiveSubstatements(statements);
         }
     }
 
+    private void doAddEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
+        final Collection<? extends StatementContextBase<?, ?, ?>> casted =
+            (Collection<? extends StatementContextBase<?, ?, ?>>) statements;
+        final ModelProcessingPhase phase = completedPhase;
+        if (phase != null) {
+            for (StatementContextBase<?, ?, ?> stmt : casted) {
+                ensureCompletedPhase(stmt, phase);
+            }
+        }
+
+        effective.addAll(casted);
+    }
+
     // Make sure target statement has transitioned at least to specified phase. This method is just before we take
     // allow a statement to become our substatement. This is needed to ensure that every statement tree does not contain
     // any statements which did not complete the same phase as the root statement.
@@ -507,29 +524,40 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
     private void beforeAddEffectiveStatement(final int toAdd) {
         // We cannot allow statement to be further mutated
-        final StatementSourceReference ref = getStatementSourceReference();
-        verify(completedPhase != ModelProcessingPhase.EFFECTIVE_MODEL, "Cannot modify finished statement at %s", ref);
+        verify(completedPhase != ModelProcessingPhase.EFFECTIVE_MODEL, "Cannot modify finished statement at %s",
+            getStatementSourceReference());
+        beforeAddEffectiveStatementUnsafe(toAdd);
+    }
 
+    private void beforeAddEffectiveStatementUnsafe(final int toAdd) {
         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
         checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
                 || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL,
-                "Effective statement cannot be added in declared phase at: %s", ref);
+                "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference());
 
         if (effective.isEmpty()) {
             effective = new ArrayList<>(toAdd);
         }
     }
 
-    // Exists only due to memory optimization
+    // These two exists only due to memory optimization, should live in AbstractResumedStatement
     final boolean fullyDefined() {
         return fullyDefined;
     }
 
-    // Exists only due to memory optimization, should live in AbstractResumedStatement
     final void setFullyDefined() {
         fullyDefined = true;
     }
 
+    // These two exist only due to memory optimization, should live in InferredStatementContext
+    final boolean substatementsInitialized() {
+        return substatementsInitialized;
+    }
+
+    final void setSubstatementsInitialized() {
+        substatementsInitialized = true;
+    }
+
     @Override
     public E buildEffective() {
         final E existing;