From 98b0f1f647f6b174689dfda88dda2e8f99f8e7a8 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 30 Oct 2020 02:20:00 +0100 Subject: [PATCH] Perform partial substatement initialization When we have a request for a schema tree participant, we should not force materialization of We should not need to initialize all substatements when we are being asked for a single one. Side-step this need by keeping a separate map of statements we have initialized and meshing it together if need be. JIRA: YANGTOOLS-1160 Change-Id: I8e014650272409ba30e528341dcba9c05311971b Signed-off-by: Robert Varga --- .../reactor/AbstractResumedStatement.java | 36 ++- .../reactor/InferredStatementContext.java | 209 +++++++++++++++--- .../stmt/reactor/StatementContextBase.java | 144 +++++------- 3 files changed, 262 insertions(+), 127 deletions(-) diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/AbstractResumedStatement.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/AbstractResumedStatement.java index 58d8c293b6..092c8a6108 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/AbstractResumedStatement.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/AbstractResumedStatement.java @@ -10,7 +10,9 @@ package org.opendaylight.yangtools.yang.parser.stmt.reactor; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import com.google.common.collect.ImmutableList; import java.util.Collection; +import java.util.List; import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; @@ -39,6 +41,7 @@ abstract class AbstractResumedStatement, E ext private final @NonNull StatementSourceReference statementDeclSource; private final String rawArgument; + private List> effective = ImmutableList.of(); private StatementMap substatements = StatementMap.empty(); private @Nullable D declaredInstance; @@ -90,6 +93,32 @@ abstract class AbstractResumedStatement, E ext return substatements; } + @Override + public final Collection> mutableEffectiveSubstatements() { + return mutableEffectiveSubstatements(effective); + } + + @Override + public final void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) { + effective = removeStatementFromEffectiveSubstatements(effective, statementDef); + } + + @Override + public final void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef, + final String statementArg) { + effective = removeStatementFromEffectiveSubstatements(effective, statementDef, statementArg); + } + + @Override + public final void addEffectiveSubstatement(final Mutable substatement) { + effective = addEffectiveSubstatement(effective, substatement); + } + + @Override + final void addEffectiveSubstatementsImpl(final Collection> statements) { + effective = addEffectiveSubstatementsImpl(effective, statements); + } + @Override public final D buildDeclared() { final D existing; @@ -154,7 +183,12 @@ abstract class AbstractResumedStatement, E ext @Override final boolean hasEmptySubstatements() { - return substatements.size() == 0 && hasEmptyEffectiveSubstatements(); + return substatements.size() == 0 && effective.isEmpty(); + } + + @Override + final Iterable> effectiveChildrenToComplete() { + return effective; } /** diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java index 472e407cbd..d8342c7fd6 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java @@ -7,16 +7,21 @@ */ package org.opendaylight.yangtools.yang.parser.stmt.reactor; +import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; +import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.model.api.SchemaPath; @@ -25,12 +30,15 @@ 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.SchemaTreeNamespace; 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.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.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextDefaults; +import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,6 +59,16 @@ final class InferredStatementContext, E extend private final QNameModule targetModule; private final A argument; + /** + * Effective substatements, lazily materialized. This field can have three states: + *
    + *
  • it can be {@code null}, in which case no materialization has taken place
  • + *
  • it can be a {@link HashMap}, in which case partial materialization has taken place
  • + *
  • it can be a {@link List}, in which case full materialization has taken place
  • + *
+ */ + private Object substatements; + private InferredStatementContext(final InferredStatementContext original, final StatementContextBase parent) { super(original); @@ -60,7 +78,8 @@ final class InferredStatementContext, E extend this.prototype = original.prototype; this.originalCtx = original.originalCtx; this.argument = original.argument; - setSubstatementsInitialized(); + // Substatements are initialized here + this.substatements = ImmutableList.of(); } InferredStatementContext(final StatementContextBase parent, final StatementContextBase prototype, @@ -73,8 +92,6 @@ final class InferredStatementContext, E extend this.childCopyType = requireNonNull(childCopyType); this.targetModule = targetModule; this.originalCtx = prototype.getOriginalCtx().orElse(prototype); - - // Note: substatements from prototype are initialized lazily through ensureSubstatements() } @Override @@ -82,6 +99,11 @@ final class InferredStatementContext, E extend return ImmutableList.of(); } + @Override + public Collection> mutableEffectiveSubstatements() { + return mutableEffectiveSubstatements(ensureEffectiveSubstatements()); + } + @Override public Iterable> allSubstatements() { // No need to concat with declared @@ -123,6 +145,28 @@ final class InferredStatementContext, E extend return originalCtx.buildDeclared(); } + @Override + public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) { + substatements = removeStatementFromEffectiveSubstatements(ensureEffectiveSubstatements(), statementDef); + } + + @Override + public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef, + final String statementArg) { + substatements = removeStatementFromEffectiveSubstatements(ensureEffectiveSubstatements(), statementDef, + statementArg); + } + + @Override + public void addEffectiveSubstatement(final Mutable substatement) { + substatements = addEffectiveSubstatement(ensureEffectiveSubstatements(), substatement); + } + + @Override + void addEffectiveSubstatementsImpl(final Collection> statements) { + substatements = addEffectiveSubstatementsImpl(ensureEffectiveSubstatements(), statements); + } + @Override InferredStatementContext reparent(final StatementContextBase newParent) { return new InferredStatementContext<>(this, newParent); @@ -130,14 +174,16 @@ final class InferredStatementContext, E extend @Override boolean hasEmptySubstatements() { - ensureEffectiveSubstatements(); - return hasEmptyEffectiveSubstatements(); + if (substatements == null) { + return prototype.hasEmptySubstatements(); + } + return substatements instanceof HashMap ? false : ((List) substatements).isEmpty(); } @Override public > @NonNull Optional findSubstatementArgument( final @NonNull Class type) { - if (substatementsInitialized()) { + if (substatements instanceof List) { return StmtContextDefaults.findSubstatementArgument(this, type); } @@ -154,61 +200,95 @@ final class InferredStatementContext, E extend @Override public boolean hasSubstatement(final @NonNull Class> type) { - return substatementsInitialized() ? StmtContextDefaults.hasSubstatement(prototype, type) + return substatements instanceof List ? StmtContextDefaults.hasSubstatement(prototype, type) + // We do not allow deletion of partially-materialized statements, hence this is accurate : prototype.hasSubstatement(type); } @Override - public , E extends EffectiveStatement> - StmtContext requestSchemaTreeChild(final QName qname) { - LOG.debug("Materializing on lookup of {}", qname); - // FIXME: YANGTOOLS-1160: we do not want to force full materialization here - ensureEffectiveSubstatements(); - - // 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) stmt; - } + public , Z extends EffectiveStatement> + StmtContext requestSchemaTreeChild(final QName qname) { + if (substatements instanceof List) { + // We have performed materialization, hence we have triggered creation of all our schema tree child + // statements. + return null; + } + + final QName templateQName = qname.bindTo(StmtContextUtils.getRootModuleQName(prototype)); + LOG.debug("Materializing child {} from {}", qname, templateQName); + + final StmtContext template; + if (prototype instanceof InferredStatementContext) { + // Note: we need to access namespace here, as the target statement may have already been populated, in which + // case we want to obtain the statement in local namespace storage. + template = (StmtContext) ((InferredStatementContext) prototype).getFromNamespace( + SchemaTreeNamespace.class, templateQName); + } else { + template = prototype.allSubstatementsStream() + .filter(stmt -> stmt.producesEffective(SchemaTreeEffectiveStatement.class) + && templateQName.equals(stmt.getStatementArgument())) + .findAny() + .orElse(null); } - return null; + + if (template == null) { + // We do not have a template, this child does not exist. It may be added later, but that is someone else's + // responsibility. + LOG.debug("Child {} does not have a template", qname); + return null; + } + + @SuppressWarnings("unchecked") + final Mutable ret = (Mutable) copySubstatement((Mutable) template) + .orElseThrow(() -> new InferenceException(getStatementSourceReference(), + "Failed to materialize child %s template %s", qname, template)); + ensureCompletedPhase(ret); + addMaterialized(template, ret); + + LOG.debug("Child {} materialized", qname); + return ret; } // 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. - @Override - void ensureEffectiveSubstatements() { - if (!substatementsInitialized()) { - initializeSubstatements(); - } + private List> ensureEffectiveSubstatements() { + return substatements instanceof List ? castEffective(substatements) + : initializeSubstatements(castMaterialized(substatements)); } @Override Iterable> effectiveChildrenToComplete() { // When we have not initialized, there are no statements to catch up: we will catch up when we are copying - // from prototype (which is already at ModelProcessingPhase.EFFECTIVE_MODEL) - return substatementsInitialized() ? super.effectiveChildrenToComplete() : List.of(); + // from prototype (which is already at ModelProcessingPhase.EFFECTIVE_MODEL). + if (substatements == null) { + return ImmutableList.of(); + } else if (substatements instanceof HashMap) { + return castMaterialized(substatements).values(); + } else { + return castEffective(substatements); + } } - private void initializeSubstatements() { + private List> initializeSubstatements( + final Map, StatementContextBase> materializedSchemaTree) { final Collection> declared = prototype.mutableDeclaredSubstatements(); final Collection> effective = prototype.mutableEffectiveSubstatements(); final List> buffer = new ArrayList<>(declared.size() + effective.size()); for (final Mutable stmtContext : declared) { if (stmtContext.isSupportedByFeatures()) { - copySubstatement(stmtContext, buffer); + copySubstatement(stmtContext, buffer, materializedSchemaTree); } } for (final Mutable stmtContext : effective) { - copySubstatement(stmtContext, buffer); + copySubstatement(stmtContext, buffer, materializedSchemaTree); } - // 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); + final List> ret = beforeAddEffectiveStatementUnsafe(ImmutableList.of(), + buffer.size()); + ret.addAll((Collection) buffer); + substatements = ret; + return ret; } // Statement copy mess starts here @@ -222,7 +302,8 @@ final class InferredStatementContext, E extend YangStmtMapping.TYPEDEF, YangStmtMapping.USES); - private void copySubstatement(final Mutable substatement, final Collection> buffer) { + private void copySubstatement(final Mutable substatement, final Collection> buffer, + final Map, StatementContextBase> materializedSchemaTree) { final StatementDefinition def = substatement.getPublicDefinition(); // FIXME: YANGTOOLS-652: formerly known as "isReusedByUses" @@ -232,7 +313,63 @@ final class InferredStatementContext, E extend return; } - substatement.copyAsChildOf(this, childCopyType, targetModule).ifPresent(buffer::add); + // Consult materialized substatements. We are in a copy operation and will end up throwing materialized + // statements away -- hence we do not perform Map.remove() to save ourselves a mutation operation. + // + // We could also perform a Map.containsKey() and perform a bulk add, but that would mean the statement order + // against parent would change -- and we certainly do not want that to happen. + final StatementContextBase materialized = findMaterialized(materializedSchemaTree, substatement); + if (materialized == null) { + copySubstatement(substatement).ifPresent(copy -> { + ensureCompletedPhase(copy); + buffer.add(copy); + }); + } else { + buffer.add(materialized); + } + } + + private Optional> copySubstatement(final Mutable substatement) { + return substatement.copyAsChildOf(this, childCopyType, targetModule); + } + + private void addMaterialized(final StmtContext template, final Mutable copy) { + final HashMap, StatementContextBase> materializedSchemaTree; + if (substatements == null) { + // Lazy initialization of backing map. We do not expect this to be used often or multiple times -- each hit + // here means an inference along schema tree, such as deviate/augment. HashMap requires power-of-two and + // defaults to 0.75 load factor -- we therefore size it to 4, i.e. next two inserts will not cause a + // resizing operation. + materializedSchemaTree = new HashMap<>(4); + substatements = materializedSchemaTree; + } else { + verify(substatements instanceof HashMap, "Unexpected substatements %s", substatements); + materializedSchemaTree = castMaterialized(substatements); + } + + final StmtContext existing = materializedSchemaTree.put(template, + (StatementContextBase) copy); + if (existing != null) { + throw new VerifyException( + "Unexpected duplicate request for " + copy.getStatementArgument() + " previous result was " + existing); + } + } + + private static @Nullable StatementContextBase findMaterialized( + final Map, StatementContextBase> materializedSchemaTree, + final StmtContext template) { + return materializedSchemaTree == null ? null : materializedSchemaTree.get(template); + } + + @SuppressWarnings("unchecked") + private static List> castEffective(final Object substatements) { + return (List>) substatements; + } + + @SuppressWarnings("unchecked") + private static HashMap, StatementContextBase> castMaterialized( + final Object substatements) { + return (HashMap, StatementContextBase>) substatements; } // Statement copy mess ends here diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java index c112199ce5..f2053f767e 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java @@ -141,9 +141,6 @@ public abstract class StatementContextBase, E private Multimap phaseListeners = ImmutableMultimap.of(); private Multimap phaseMutation = ImmutableMultimap.of(); - // Note: this field is accessed either directly, or under substatementsInitialized == true - private List> effective = ImmutableList.of(); - private List> effectOfStatement = ImmutableList.of(); private @Nullable ModelProcessingPhase completedPhase; @@ -157,9 +154,6 @@ public abstract class StatementContextBase, 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; @@ -373,28 +367,22 @@ public abstract class StatementContextBase, E return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, key); } - @Override - public final Collection> mutableEffectiveSubstatements() { - ensureEffectiveSubstatements(); - if (effective instanceof ImmutableCollection) { - return effective; - } - - return Collections.unmodifiableCollection(effective); + static final Collection> mutableEffectiveSubstatements( + final List> effective) { + return effective instanceof ImmutableCollection ? effective : Collections.unmodifiableCollection(effective); } - private void shrinkEffective() { - // Initialization guarded by all callers - if (effective.isEmpty()) { - effective = ImmutableList.of(); - } + private static List> shrinkEffective( + final List> effective) { + return effective.isEmpty() ? ImmutableList.of() : effective; } - // Note: has side-effect of ensureEffectiveSubstatements() - public final void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) { - ensureEffectiveSubstatements(); + public abstract void removeStatementFromEffectiveSubstatements(StatementDefinition statementDef); + + static final List> removeStatementFromEffectiveSubstatements( + final List> effective, final StatementDefinition statementDef) { if (effective.isEmpty()) { - return; + return effective; } final Iterator> iterator = effective.iterator(); @@ -405,7 +393,7 @@ public abstract class StatementContextBase, E } } - shrinkEffective(); + return shrinkEffective(effective); } /** @@ -420,17 +408,18 @@ public abstract class StatementContextBase, E * @param statementDef statement definition of the statement context to remove * @param statementArg statement argument of the statement context to remove */ - public final void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef, + public abstract void removeStatementFromEffectiveSubstatements(StatementDefinition statementDef, + String statementArg); + + static final List> removeStatementFromEffectiveSubstatements( + final List> effective, final StatementDefinition statementDef, final String statementArg) { if (statementArg == null) { - // Note: has side-effect of ensureEffectiveSubstatements() - removeStatementFromEffectiveSubstatements(statementDef); - } else { - ensureEffectiveSubstatements(); + return removeStatementFromEffectiveSubstatements(effective, statementDef); } if (effective.isEmpty()) { - return; + return effective; } final Iterator> iterator = effective.iterator(); @@ -441,7 +430,7 @@ public abstract class StatementContextBase, E } } - shrinkEffective(); + return shrinkEffective(effective); } // YANG example: RPC/action statements always have 'input' and 'output' defined @@ -461,22 +450,23 @@ public abstract class StatementContextBase, E * Adds an effective statement to collection of substatements. * * @param substatement substatement - * @throws IllegalStateException - * if added in declared phase - * @throws NullPointerException - * if statement parameter is null + * @throws IllegalStateException if added in declared phase + * @throws NullPointerException if statement parameter is null */ - public final void addEffectiveSubstatement(final Mutable substatement) { + public abstract void addEffectiveSubstatement(Mutable substatement); + + final List> addEffectiveSubstatement( + final List> effective, final Mutable substatement) { verifyStatement(substatement); - ensureEffectiveSubstatements(); - beforeAddEffectiveStatement(1); + final List> resized = beforeAddEffectiveStatement(effective, 1); final StatementContextBase stmt = (StatementContextBase) substatement; final ModelProcessingPhase phase = completedPhase; if (phase != null) { ensureCompletedPhase(stmt, phase); } - effective.add(stmt); + resized.add(stmt); + return resized; } /** @@ -491,35 +481,16 @@ public abstract class StatementContextBase, E public final void addEffectiveSubstatements(final Collection> statements) { if (!statements.isEmpty()) { statements.forEach(StatementContextBase::verifyStatement); - ensureEffectiveSubstatements(); - beforeAddEffectiveStatement(statements.size()); - doAddEffectiveSubstatements(statements); + addEffectiveSubstatementsImpl(statements); } } - // exposed for InferredStatementContext, which we expect to initialize effective substatements - void ensureEffectiveSubstatements() { - // No-op for everything except InferredStatementContext - } - - // Exposed for InferredStatementContextr only, others do not need initialization - Iterable> effectiveChildrenToComplete() { - return effective; - } + abstract void addEffectiveSubstatementsImpl(Collection> statements); - // exposed for InferredStatementContext only - final void addInitialEffectiveSubstatements(final Collection> statements) { - verify(!substatementsInitialized, "Attempted to re-initialized statement {} with {}", this, statements); - substatementsInitialized = true; - - if (!statements.isEmpty()) { - statements.forEach(StatementContextBase::verifyStatement); - beforeAddEffectiveStatementUnsafe(statements.size()); - doAddEffectiveSubstatements(statements); - } - } - - private void doAddEffectiveSubstatements(final Collection> statements) { + final List> addEffectiveSubstatementsImpl( + final List> effective, + final Collection> statements) { + final List> resized = beforeAddEffectiveStatement(effective, statements.size()); final Collection> casted = (Collection>) statements; final ModelProcessingPhase phase = completedPhase; @@ -529,8 +500,19 @@ public abstract class StatementContextBase, E } } - // Initialization guarded by all callers - effective.addAll(casted); + resized.addAll(casted); + return resized; + } + + abstract Iterable> effectiveChildrenToComplete(); + + // exposed for InferredStatementContext only + final void ensureCompletedPhase(final Mutable stmt) { + verifyStatement(stmt); + final ModelProcessingPhase phase = completedPhase; + if (phase != null) { + ensureCompletedPhase((StatementContextBase) stmt, phase); + } } // Make sure target statement has transitioned at least to specified phase. This method is just before we take @@ -545,23 +527,22 @@ public abstract class StatementContextBase, E verify(stmt instanceof StatementContextBase, "Unexpected statement %s", stmt); } - private void beforeAddEffectiveStatement(final int toAdd) { + private List> beforeAddEffectiveStatement( + final List> effective, final int toAdd) { // We cannot allow statement to be further mutated verify(completedPhase != ModelProcessingPhase.EFFECTIVE_MODEL, "Cannot modify finished statement at %s", getStatementSourceReference()); - beforeAddEffectiveStatementUnsafe(toAdd); + return beforeAddEffectiveStatementUnsafe(effective, toAdd); } - private void beforeAddEffectiveStatementUnsafe(final int toAdd) { + final List> beforeAddEffectiveStatementUnsafe( + final List> effective, 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", getStatementSourceReference()); - // Initialization guarded by all callers - if (effective.isEmpty()) { - effective = new ArrayList<>(toAdd); - } + return effective.isEmpty() ? new ArrayList<>(toAdd) : effective; } // These two exists only due to memory optimization, should live in AbstractResumedStatement @@ -573,15 +554,6 @@ public abstract class StatementContextBase, E 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; @@ -975,14 +947,6 @@ public abstract class StatementContextBase, E */ abstract boolean hasEmptySubstatements(); - // Dual use method: AbstractResumedStatement does not use 'initialized' and InferredStatementContext ensures - // initialization. - // FIXME: 7.0.0: I think this warrants a separate subclasses, as InferredStatementContext wants to manage these - // itself. Before we do that, though, we need to analyze size impacts - final boolean hasEmptyEffectiveSubstatements() { - return effective.isEmpty(); - } - /** * Config statements are not all that common which means we are performing a recursive search towards the root * every time {@link #isConfiguration()} is invoked. This is quite expensive because it causes a linear search -- 2.36.6