Skip to content

Commit 11f14a6

Browse files
Employ Reflect for enum hackery
1 parent 47f1071 commit 11f14a6

File tree

1 file changed

+4
-146
lines changed

1 file changed

+4
-146
lines changed

src/main/java/com/cleanroommc/hackery/enums/EnumHackery.java

Lines changed: 4 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,17 @@
11
package com.cleanroommc.hackery.enums;
22

3-
import com.cleanroommc.hackery.ReflectionHackery;
4-
import jdk.internal.reflect.ReflectionFactory;
5-
import net.minecraftforge.fml.common.FMLLog;
6-
import org.apache.commons.lang3.ArrayUtils;
7-
8-
import java.lang.invoke.MethodHandle;
9-
import java.lang.invoke.MethodHandles;
10-
import java.lang.reflect.Constructor;
11-
import java.lang.reflect.Field;
12-
import java.lang.reflect.Method;
13-
import java.util.Arrays;
14-
import java.util.Map;
3+
import net.lenni0451.reflect.Enums;
154

165
public final class EnumHackery {
176

18-
public static final ReflectionFactory factory = ReflectionFactory.getReflectionFactory();
19-
20-
private static final Field class$enumConstants, class$enumConstantDirectory, enumVars$cachedEnumConstants, enumVars$cachedEnumConstantDirectory;
21-
private static final Method constructorAccessor$newInstance, class$enumConstantDirectory_method, class$getEnumVars;
22-
23-
private static final Class[] prefix = {String.class, int.class};
24-
25-
static {
26-
Field constructorAccessor;
27-
Field enumConstants;
28-
Field enumConstantDirectory;
29-
Field enumConstantsj9;
30-
Field enumConstantDirectoryj9;
31-
Method newInstance;
32-
Method enumConstantDirectoryMethod;
33-
Method enumVars;
34-
try {
35-
constructorAccessor = ReflectionHackery.deepSearchForField(Constructor.class, field -> "constructorAccessor".equals(field.getName()), true);
36-
37-
if (System.getProperty("java.vendor").startsWith("IBM")){
38-
enumVars = Class.class.getDeclaredMethod("getEnumVars");
39-
enumVars.setAccessible(true);
40-
enumConstantsj9 = enumVars.getReturnType().getDeclaredField("cachedEnumConstants");
41-
enumConstantsj9.setAccessible(true);
42-
enumConstantDirectoryj9 = enumVars.getReturnType().getDeclaredField("cachedEnumConstantDirectory");
43-
enumConstantsj9.setAccessible(true);
44-
enumConstants = null;
45-
enumConstantDirectory = null;
46-
} else {
47-
enumConstants = Class.class.getDeclaredField("enumConstants");
48-
enumConstants.setAccessible(true);
49-
50-
enumConstantDirectory = Class.class.getDeclaredField("enumConstantDirectory");
51-
enumConstantDirectory.setAccessible(true);
52-
53-
enumConstantsj9 = null;
54-
enumConstantDirectoryj9 = null;
55-
enumVars = null;
56-
}
57-
58-
59-
newInstance = constructorAccessor.getType().getMethod("newInstance", Object[].class);
60-
61-
enumConstantDirectoryMethod = Class.class.getDeclaredMethod("enumConstantDirectory");
62-
enumConstantDirectoryMethod.setAccessible(true);
63-
} catch (ReflectiveOperationException e) {
64-
throw new RuntimeException(e);
65-
}
66-
67-
class$enumConstants = enumConstants;
68-
class$enumConstantDirectory = enumConstantDirectory;
69-
class$getEnumVars = enumVars;
70-
enumVars$cachedEnumConstants = enumConstantsj9;
71-
enumVars$cachedEnumConstantDirectory = enumConstantDirectoryj9;
72-
constructorAccessor$newInstance = newInstance;
73-
class$enumConstantDirectory_method = enumConstantDirectoryMethod;
74-
}
75-
767
public static <T extends Enum<T>> T addEnumEntry(Class<T> enumClass, String enumName) {
778
return addEnumEntry(enumClass, enumName, new Class[0], new Object[0]);
789
}
7910

80-
@SuppressWarnings("unchecked")
8111
public static <T extends Enum<T>> T addEnumEntry(Class<T> enumClass, String enumName, Class<?>[] parameterTypes, Object[] parameterValues) {
82-
if (parameterTypes.length != parameterValues.length) {
83-
throw new IllegalArgumentException("The amount of parameter types must be the same as the parameter values.");
84-
}
85-
86-
try {
87-
Constructor<T> constructor = enumClass.getDeclaredConstructor(ArrayUtils.addAll(prefix, parameterTypes));
88-
constructor.setAccessible(true);
89-
MethodHandle handle = MethodHandles.lookup().unreflectConstructor(constructor);
90-
Method m = enumClass.getMethod("values");
91-
Object o = m.invoke(enumClass);
92-
T instance = (T) handle.withVarargs(false).invokeWithArguments(ArrayUtils.addAll(new Object[]{enumName, ((Object[]) o).length}, parameterValues));
93-
94-
Field nameField = Enum.class.getDeclaredField("name");
95-
nameField.setAccessible(true);
96-
nameField.set(instance, enumName);
97-
98-
Field valuesField = enumClass.getDeclaredField("$VALUES");
99-
valuesField.setAccessible(true);
100-
101-
Field ordinalField = Enum.class.getDeclaredField("ordinal");
102-
ordinalField.setAccessible(true);
103-
104-
// we get the current Enum[]
105-
T[] values = (T[]) valuesField.get(null);
106-
for (int i = 0; i < values.length; i++) {
107-
T value = values[i];
108-
if (value.name().equals(enumName)) {
109-
//setOrdinal(enumName, value.ordinal());
110-
ordinalField.set(instance, value.ordinal());
111-
values[i] = instance;
112-
Field[] fields = enumClass.getDeclaredFields();
113-
for (Field field : fields) {
114-
if (field.getName().equals(enumName)) {
115-
ReflectionHackery.setField(field, null, instance);
116-
}
117-
}
118-
return instance;
119-
}
120-
}
121-
122-
// we did not find it in the existing array, thus
123-
// append it to the array
124-
T[] newValues = Arrays.copyOf(values, values.length + 1);
125-
newValues[newValues.length - 1] = instance;
126-
ReflectionHackery.setField(valuesField, null, newValues);
127-
if (System.getProperty("java.vendor").startsWith("IBM")){
128-
Object enumVar = class$getEnumVars.invoke(enumClass);
129-
ReflectionHackery.setField(enumVars$cachedEnumConstants, enumVar, newValues);
130-
}else {
131-
// Add new enum to cache
132-
ReflectionHackery.setField(class$enumConstants, enumClass, newValues);
133-
}
134-
135-
// Ensure the cache exists
136-
Map<String, T> directory = (Map<String, T>) class$enumConstantDirectory_method.invoke(enumClass);
137-
// Add new enum to cache
138-
directory.put(enumName, instance);
139-
140-
int ordinal = newValues.length - 1;
141-
ordinalField.set(instance, ordinal);
142-
143-
return instance;
144-
145-
} catch (Throwable e) {
146-
FMLLog.log.error(e);
147-
throw new RuntimeException(e);
148-
}
149-
150-
}
151-
152-
public static <T extends Enum<T>> void resetEnumRelatedCaches(Class<T> enumClass) throws ReflectiveOperationException {
153-
if (class$enumConstants != null){
154-
class$enumConstants.set(enumClass, null);
155-
class$enumConstantDirectory.set(enumClass, null);
156-
}
12+
T o = Enums.newInstance(enumClass, enumName, enumClass.getEnumConstants().length, parameterTypes, parameterValues);
13+
Enums.addEnumInstance(enumClass, o);
14+
return o;
15715
}
15816

15917
private EnumHackery() { }

0 commit comments

Comments
 (0)