|
6 | 6 | import java.util.ArrayList; |
7 | 7 | import java.util.Collection; |
8 | 8 | import java.util.Collections; |
9 | | -import java.util.HashMap; |
| 9 | +import java.util.LinkedHashMap; |
10 | 10 | import java.util.List; |
11 | 11 | import java.util.Map; |
12 | 12 |
|
@@ -85,10 +85,8 @@ static ResolveResult<? extends AbstractConfigValue> resolveSubstitutions(Replace |
85 | 85 | AbstractConfigValue merged = null; |
86 | 86 | for (AbstractConfigValue end : stack) { |
87 | 87 | // Per the HOCON spec, a substitution hidden by a value that |
88 | | - // cannot be merged with it is never evaluated. Once the |
89 | | - // accumulated merged value ignores fallbacks, nothing below it |
90 | | - // in the stack can contribute, so skip it to avoid evaluating |
91 | | - // substitutions that cannot affect the result. |
| 88 | + // cannot be merged with it is never evaluated. If merged already |
| 89 | + // ignores fallbacks, nothing below can contribute, so stop. |
92 | 90 | if (merged != null && merged.ignoresFallbacks()) { |
93 | 91 | if (ConfigImpl.traceSubstitutionsEnabled()) |
94 | 92 | ConfigImpl.trace(newContext.depth(), |
@@ -135,17 +133,11 @@ else if (end instanceof Unmergeable) { |
135 | 133 |
|
136 | 134 | sourceForEnd = source.pushParent(replaceable); |
137 | 135 |
|
138 | | - // Per the HOCON spec, a substitution hidden by a value that |
139 | | - // cannot be merged with it is never evaluated. When merging |
140 | | - // objects field-by-field the merged object may already contain, |
141 | | - // at some keys, values that ignore fallbacks (e.g. a resolved |
142 | | - // non-object shadowing a substitution below). Any substitution |
143 | | - // at those same keys in a lower-priority object on the stack |
144 | | - // would be discarded by the subsequent merge anyway, so we |
145 | | - // prune those keys before resolving to avoid evaluating |
146 | | - // substitutions that cannot contribute to the result. |
147 | | - if (merged instanceof AbstractConfigObject && end instanceof AbstractConfigObject |
148 | | - && !(end instanceof Unmergeable)) { |
| 136 | + // Same spec rule as the short-circuit above, applied per-key: |
| 137 | + // keys in the lower-priority 'end' object that are already |
| 138 | + // shadowed in 'merged' by a value ignoring fallbacks would be |
| 139 | + // discarded by the subsequent merge, so don't resolve them. |
| 140 | + if (merged instanceof AbstractConfigObject && end instanceof AbstractConfigObject) { |
149 | 141 | AbstractConfigObject prunedEnd = pruneShadowedKeys( |
150 | 142 | (AbstractConfigObject) end, (AbstractConfigObject) merged); |
151 | 143 | if (prunedEnd == null) { |
@@ -198,7 +190,7 @@ private static AbstractConfigObject pruneShadowedKeys(AbstractConfigObject end, |
198 | 190 | if (!(end instanceof SimpleConfigObject)) |
199 | 191 | return end; |
200 | 192 | SimpleConfigObject simple = (SimpleConfigObject) end; |
201 | | - Map<String, AbstractConfigValue> kept = new HashMap<String, AbstractConfigValue>(); |
| 193 | + Map<String, AbstractConfigValue> kept = new LinkedHashMap<String, AbstractConfigValue>(); |
202 | 194 | boolean pruned = false; |
203 | 195 | for (String key : simple.keySet()) { |
204 | 196 | AbstractConfigValue mergedValue; |
|
0 commit comments