Skip to content

Commit 1aafa44

Browse files
committed
Handle enum reordering in ClassInitPlugin
1 parent e2ba12a commit 1aafa44

File tree

2 files changed

+82
-12
lines changed

2 files changed

+82
-12
lines changed

hotswap-agent-core/src/main/java/org/hotswap/agent/plugin/jvm/ClassInitPlugin.java

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.io.IOException;
2222
import java.lang.reflect.Field;
2323
import java.lang.reflect.Method;
24+
import java.util.ArrayList;
25+
import java.util.List;
2426

2527
import org.hotswap.agent.annotation.LoadEvent;
2628
import org.hotswap.agent.annotation.OnClassLoadEvent;
@@ -70,6 +72,11 @@ public static void patch(final CtClass ctClass, final ClassLoader classLoader, f
7072
// swallow
7173
}
7274

75+
final boolean enumReordered = isEnumReordered(ctClass, originalClass);
76+
if (enumReordered) {
77+
LOGGER.debug("Enum order changed for {} -> will reinitialize enum constant fields.", className);
78+
}
79+
7380
CtConstructor clinit = ctClass.getClassInitializer();
7481

7582
if (clinit != null) {
@@ -102,11 +109,17 @@ public void edit(FieldAccess f) throws CannotCompileException {
102109
if (reinitializeStatics[0]) {
103110
LOGGER.debug("New field will be initialized {}", f.getFieldName());
104111
} else {
105-
reinitializeStatics[0] = checkOldEnumValues(ctClass, originalClass);
112+
if (enumReordered) {
113+
reinitializeStatics[0] = true;
114+
} else {
115+
reinitializeStatics[0] = checkOldEnumValues(ctClass, originalClass);
116+
}
106117
}
107118
} else {
108-
LOGGER.debug("Skipping old field {}", f.getFieldName());
109-
f.replace("{}");
119+
if (!originalField.isEnumConstant() || !enumReordered) {
120+
LOGGER.debug("Skipping old field {}", f.getFieldName());
121+
f.replace("{}");
122+
}
110123
}
111124
}
112125
}
@@ -165,11 +178,47 @@ private static boolean checkOldEnumValues(CtClass ctClass, Class<?> originalClas
165178
return false;
166179
}
167180

181+
private static boolean isEnumReordered(CtClass ctClass, Class<?> originalClass) {
182+
if (!ctClass.isEnum() || originalClass == null || !originalClass.isEnum()) {
183+
return false;
184+
}
185+
186+
// Old order from runtime (original class)
187+
Enum<?>[] oldConsts = (Enum<?>[]) originalClass.getEnumConstants();
188+
List<String> oldOrder = new ArrayList<>();
189+
for (Enum<?> e : oldConsts) {
190+
oldOrder.add(e.name());
191+
}
192+
193+
List<String> newOrder = new ArrayList<>();
194+
try {
195+
for (CtField f : ctClass.getDeclaredFields()) {
196+
int mod = f.getModifiers();
197+
boolean looksLikeConst = Modifier.isStatic(mod) && Modifier.isFinal(mod) && f.getType().getName().equals(ctClass.getName());
198+
if (looksLikeConst) {
199+
newOrder.add(f.getName());
200+
}
201+
}
202+
} catch (NotFoundException ex) {
203+
return true;
204+
}
205+
206+
for (int i = 0; i < newOrder.size() && i < oldOrder.size(); i++) {
207+
if (!newOrder.get(i).equals(oldOrder.get(i))) {
208+
return true;
209+
}
210+
}
211+
212+
return false;
213+
}
214+
215+
168216
private static boolean isSyntheticClass(Class<?> classBeingRedefined) {
169217
return classBeingRedefined.getSimpleName().contains("$$_javassist")
170218
|| classBeingRedefined.getSimpleName().contains("$$_jvst")
171219
|| classBeingRedefined.getName().startsWith("com.sun.proxy.$Proxy")
172220
|| classBeingRedefined.getSimpleName().contains("$$");
173221
}
174222

223+
175224
}

hotswap-agent-core/src/test/java/org/hotswap/agent/plugin/jvm/classinit/ClassInitTest.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.hotswap.agent.plugin.jvm.ClassInitPlugin;
2727
import org.hotswap.agent.util.ReflectionHelper;
2828
import org.hotswap.agent.util.test.WaitHelper;
29-
import org.junit.Test;
3029

3130
public class ClassInitTest {
3231

@@ -50,11 +49,25 @@ public void testStatic1() throws Exception {
5049
public void testEnumAddRemove() throws Exception {
5150
Enum1 enum1 = Enum1.ITEM_1;
5251
ClassInitPlugin.reloadFlag = true;
53-
int lenEnum1 = Enum1.values().length;
52+
53+
int ordinal = 0;
54+
for (Enum1 e : Enum1.values()) {
55+
System.out.printf("%s: 0x%08x%n", e.name(), System.identityHashCode(e));
56+
assertEquals(ordinal, e.ordinal());
57+
ordinal++;
58+
}
5459

5560
swapClasses(Enum1.class, Enum2.class.getName());
5661

5762
assertEquals(enum1.values().length, Enum2.values().length);
63+
assertEquals(Enum1.class.getEnumConstants().length, Enum2.class.getEnumConstants().length);
64+
65+
ordinal = 0;
66+
for (Enum1 e : Enum1.values()) {
67+
System.out.printf("%s: 0x%08x%n", e.name(), System.identityHashCode(e));
68+
assertEquals(ordinal, e.ordinal());
69+
ordinal++;
70+
}
5871

5972
assertEquals(ReflectionHelper.get(enum1, "ITEM_1"), enum1); // new ITEM_1 is same instance like enum1
6073

@@ -79,18 +92,26 @@ public void testEnumAddRemove() throws Exception {
7992

8093
// @Test
8194
public void testEnumRemove() throws Exception {
82-
Enum3 enum1 = Enum3.ITEM_1;
83-
Enum3 enum3 = Enum3.ITEM_3;
95+
ClassInitPlugin.reloadFlag = true;
8496

85-
int lenEnum4 = Enum4.values().length;
97+
int ordinal = 0;
98+
for (Enum3 e : Enum3.values()) {
99+
System.out.printf("%s: 0x%08x%n", e.name(), System.identityHashCode(e));
100+
assertEquals(ordinal, e.ordinal());
101+
ordinal++;
102+
}
86103

87-
ClassInitPlugin.reloadFlag = true;
88104
swapClasses(Enum3.class, Enum4.class.getName());
89105

90-
assertEquals(Enum3.values().length, lenEnum4);
106+
assertEquals(Enum3.values().length, Enum4.values().length);
107+
assertEquals(Enum3.class.getEnumConstants().length, Enum4.class.getEnumConstants().length);
91108

92-
assertEquals(Enum3.values()[0], enum1); // must be kept from original
93-
assertEquals(Enum3.values()[1], enum3); // must be kept and shifted
109+
ordinal = 0;
110+
for (Enum3 e : Enum3.values()) {
111+
System.out.printf("%s: 0x%08x%n", e.name(), System.identityHashCode(e));
112+
assertEquals(ordinal, e.ordinal());
113+
ordinal++;
114+
}
94115
}
95116

96117
private void swapClasses(Class<?> cls1, String class2Name) throws Exception {

0 commit comments

Comments
 (0)