Skip to content

Commit 71d49b0

Browse files
committed
Make ConfigurationCacheHackList safe from Java's object serialization foibles.
1 parent c8d5cee commit 71d49b0

File tree

5 files changed

+50
-8
lines changed

5 files changed

+50
-8
lines changed

lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 DiffPlug
2+
* Copyright 2024-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
1515
*/
1616
package com.diffplug.spotless;
1717

18+
import java.io.IOException;
19+
import java.io.Serializable;
1820
import java.util.ArrayList;
1921
import java.util.Collection;
2022
import java.util.List;
@@ -48,8 +50,27 @@
4850
*/
4951
public class ConfigurationCacheHackList implements java.io.Serializable {
5052
private static final long serialVersionUID = 1L;
51-
private final boolean optimizeForEquality;
52-
private final ArrayList<Object> backingList = new ArrayList<>();
53+
private boolean optimizeForEquality;
54+
public ArrayList<Object> backingList = new ArrayList<>();
55+
56+
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
57+
out.writeBoolean(optimizeForEquality);
58+
out.writeInt(backingList.size());
59+
for (Object obj : backingList) {
60+
// if write out the list on its own, we'll get java's non-deterministic object-graph serialization
61+
// by writing each object to raw bytes independently, we avoid this
62+
out.writeObject(LazyForwardingEquality.toBytes((Serializable) obj));
63+
}
64+
}
65+
66+
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
67+
optimizeForEquality = in.readBoolean();
68+
backingList = new ArrayList<>();
69+
int size = in.readInt();
70+
for (int i = 0; i < size; i++) {
71+
backingList.add(LazyForwardingEquality.fromBytes((byte[]) in.readObject()));
72+
}
73+
}
5374

5475
public static ConfigurationCacheHackList forEquality() {
5576
return new ConfigurationCacheHackList(true);

lib/src/main/java/com/diffplug/spotless/LazyForwardingEquality.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2023 DiffPlug
2+
* Copyright 2016-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
1515
*/
1616
package com.diffplug.spotless;
1717

18+
import java.io.ByteArrayInputStream;
1819
import java.io.ByteArrayOutputStream;
1920
import java.io.IOException;
2021
import java.io.ObjectInputStream;
@@ -112,6 +113,15 @@ static byte[] toBytes(Serializable obj) {
112113
return byteOutput.toByteArray();
113114
}
114115

116+
static Object fromBytes(byte[] bytes) {
117+
ByteArrayInputStream byteOutput = new ByteArrayInputStream(bytes);
118+
try (ObjectInputStream objectOutput = new ObjectInputStream(byteOutput)) {
119+
return objectOutput.readObject();
120+
} catch (IOException | ClassNotFoundException e) {
121+
throw ThrowingEx.asRuntime(e);
122+
}
123+
}
124+
115125
/** Ensures that the lazy state has been evaluated. */
116126
public static void unlazy(Object in) {
117127
if (in instanceof LazyForwardingEquality) {

lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2024 DiffPlug
2+
* Copyright 2016-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

testlib/src/main/java/com/diffplug/spotless/StepHarnessWithFile.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2024 DiffPlug
2+
* Copyright 2016-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -98,4 +98,14 @@ public StringSelfie expectLintsOfResource(String filename, String resource) {
9898
throw new AssertionError(e);
9999
}
100100
}
101+
102+
public StringSelfie expectLintsOfFileAndContent(String filename, String content) {
103+
try {
104+
File file = harness.setFile(filename).toContent(content);
105+
LintState state = LintState.of(formatter(), file);
106+
return StepHarness.expectLintsOf(state, formatter());
107+
} catch (IOException e) {
108+
throw new AssertionError(e);
109+
}
110+
}
101111
}

testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2024 DiffPlug
2+
* Copyright 2020-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
import com.diffplug.spotless.FormatterStep;
2525
import com.diffplug.spotless.ResourceHarness;
2626
import com.diffplug.spotless.StepHarness;
27+
import com.diffplug.spotless.StepHarnessWithFile;
2728

2829
class FenceStepTest extends ResourceHarness {
2930
@Test
@@ -85,7 +86,7 @@ void broken() {
8586
FormatterStep fence = FenceStep.named("fence").openClose("spotless:off", "spotless:on")
8687
.preserveWithin(Arrays.asList(ReplaceStep.create("replace", "spotless:on", "REMOVED")));
8788
// this fails because uppercase turns spotless:off into SPOTLESS:OFF, etc
88-
StepHarness.forStep(fence).expectLintsOf(StringPrinter.buildStringFromLines("A B C",
89+
StepHarnessWithFile.forStep(this, fence).expectLintsOfFileAndContent("README.md", StringPrinter.buildStringFromLines("A B C",
8990
"spotless:off",
9091
"D E F",
9192
"spotless:on",

0 commit comments

Comments
 (0)