You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A copied list of shared mutable elements is INDEPENDENT; callers that modify the elements stay @NotModified #112
fernflower (test-oss/fernflower, main sources; maddi 8a38863c8, maddi-mod 6ed8f49f), Statement:
privatefinalEnumMap<EdgeType, List<StatEdge>> mapSuccEdges = newEnumMap<>(EdgeType.class);
privateList<StatEdge> getEdges(EdgeTypetype, EdgeDirectiondirection) {
EnumMap<EdgeType, List<StatEdge>> map = direction == EdgeDirection.BACKWARD ? mapPredEdges : mapSuccEdges;
List<StatEdge> res;
if (/* one type */) {
res = map.get(type);
res = res == null ? newArrayList<>() : newArrayList<>(res); // a new list, the same StatEdge objects
} else {
res = newArrayList<>();
for (...) { List<StatEdge> lst = map.get(edgetype); if (lst != null) res.addAll(lst); }
}
returnres;
}
publicList<StatEdge> getAllSuccessorEdges() { returngetEdges(EdgeType.ALL, EdgeDirection.FORWARD); }
and LabelHelper:
privatestaticvoidresetAllEdges(Statementstat) {
for (Statementst : stat.getStats()) resetAllEdges(st);
for (StatEdgeedge : stat.getAllSuccessorEdges()) {
edge.explicit = true;
edge.labeled = true;
}
}
Results: getEdges, getAllSuccessorEdges and getSuccessorEdges have independentMethod: 2 (INDEPENDENT), and resetAllEdges has stat as unmodifiedParameter: 1 (and structurally unmodified).
The returned list is new, but its elements are the statement's own StatEdges, and StatEdge is mutable
(labeled, explicit and others are plain fields). So the result shares hidden content with the receiver: at most INDEPENDENT_HC, not INDEPENDENT. resetAllEdges writes those edges, so stat should at least not be deeply @NotModified. The same shape (a defensive copy of a list of mutable objects, handed out and modified by the
caller) recurs in fernflower's label and edge helpers. LabelHelper.setRetEdgesUnlabeled (through getAllPredecessorEdges), SwitchHelper.prepareForRules and SwitchPatternHelper.normalizeCaseLabels (through getSuccessorEdges) write StatEdge.labeled/explicit on edges reached this way while their statement parameter is
reported unmodified. LabelHelper.processStatementLabel does the same through getLabelEdges(), a different getter
that I have not checked.
Another instance of the same shape, possibly a larger one: Exprent.getAllExprents() / getAllExprents(boolean) return a new list of the expression's own (mutable) subexpressions, and have independentMethod: 2 (INDEPENDENT). A caller that modifies those subexpressions keeps its Exprent parameter @NotModified: StackVarsProcessor.setExprentVersionsToNull(Exprent) calls setVersion(0) on every VarExprent in exprent.getAllExprents(true), and exprent is reported unmodified. In fernflower 18 parameters or receivers reported unmodified are written through VarExprent.setVersion / setVarType / setLVTEntry or ConstExprent.setConstType. I have checked only setExprentVersionsToNull to go through getAllExprents; the others may reach their expressions another way.
Fixed on devel (merge 00b2a40): 774640a (a copy of a map value's mutable elements is dependent, and modifying them modifies the map's owner), c695f7a (≥ leaves a variable grouped with the container to its pass set), 2e0dca8. The parameter half was split off as #119 and decided there. Gate NOOBJECTGRAPHINDEP. Gate green on devel after the merge (laser1, 2026-11-13).
Activity
bnaudts commentedon Nov 12, 2026
Another instance of the same shape, possibly a larger one:
Exprent.getAllExprents()/getAllExprents(boolean)return a new list of the expression's own (mutable) subexpressions, and haveindependentMethod: 2(INDEPENDENT). A caller that modifies those subexpressions keeps itsExprentparameter@NotModified:StackVarsProcessor.setExprentVersionsToNull(Exprent)callssetVersion(0)on everyVarExprentinexprent.getAllExprents(true), andexprentis reported unmodified. In fernflower 18 parameters or receivers reported unmodified are written throughVarExprent.setVersion/setVarType/setLVTEntryorConstExprent.setConstType. I have checked onlysetExprentVersionsToNullto go throughgetAllExprents; the others may reach their expressions another way.🤖 Generated with Claude Code
WIP #112: a copy of a map value's mutable elements is dependent, and …
WIP #112: ≥ leaves a variable grouped with the container to its pass …
WIP #112: the ≤ independence rule leaves parameters out
Merge ws/maddi-null up to b0050ed: #112-#116, #119
Merge ws/maddi-null up to 12051ae: #112-#117, #119, logging to DEBUG
bnaudts commentedon Nov 13, 2026
Fixed on devel (merge 00b2a40): 774640a (a copy of a map value's mutable elements is dependent, and modifying them modifies the map's owner), c695f7a (≥ leaves a variable grouped with the container to its pass set), 2e0dca8. The parameter half was split off as #119 and decided there. Gate NOOBJECTGRAPHINDEP. Gate green on devel after the merge (laser1, 2026-11-13).