Describe the bug
The + and - operators on lists mutate their left operand in place instead of returning a new list. OperatorUtils.addToList calls op1.addAll(op2) and returns the same op1 instance; OperatorUtils.subtractFromList calls op1.removeAll(op2) / op1.remove(op2) similarly. Any later use of the left-hand variable then sees the mutated contents.
private static Object addToList(List<?> op1, Object op2) {
if (op2 instanceof Collection) {
op1.addAll((Collection) op2); // mutates the caller's list
} else {
((List<Object>) op1).add(op2);
}
return op1;
}
To Reproduce
PebbleEngine engine = new PebbleEngine.Builder().build();
PebbleTemplate template = engine.getLiteralTemplate("{{ a + b }} | {{ a }}");
Map<String, Object> ctx = new HashMap<>();
ctx.put("a", new ArrayList<>(List.of("x")));
ctx.put("b", List.of("y", "z"));
StringWriter writer = new StringWriter();
template.evaluate(writer, ctx);
System.out.println(writer);
Expected behavior
[x, y, z] | [x] — evaluating a + b should not change a. Template expression evaluation is expected to be side-effect free.
Actual behavior
[x, y, z] | [x, y, z] — a is mutated in place by the + operator. The same happens with - via subtractFromList.
Suggested fix
Make addToList / subtractFromList non-mutating by copying the left operand before modifying:
private static Object addToList(List<?> op1, Object op2) {
List<Object> result = new ArrayList<>(op1);
if (op2 instanceof Collection) {
result.addAll((Collection) op2);
} else {
result.add(op2);
}
return result;
}
Both methods are already @Deprecated; if the feature is to be kept, it should at least be side-effect free.
Environment
Describe the bug
The
+and-operators on lists mutate their left operand in place instead of returning a new list.OperatorUtils.addToListcallsop1.addAll(op2)and returns the sameop1instance;OperatorUtils.subtractFromListcallsop1.removeAll(op2)/op1.remove(op2)similarly. Any later use of the left-hand variable then sees the mutated contents.To Reproduce
Expected behavior
[x, y, z] | [x]— evaluatinga + bshould not changea. Template expression evaluation is expected to be side-effect free.Actual behavior
[x, y, z] | [x, y, z]—ais mutated in place by the+operator. The same happens with-viasubtractFromList.Suggested fix
Make
addToList/subtractFromListnon-mutating by copying the left operand before modifying:Both methods are already
@Deprecated; if the feature is to be kept, it should at least be side-effect free.Environment
master)