|
1 | 1 | package com.cleanroommc.hackery.enums; |
2 | 2 |
|
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; |
15 | 4 |
|
16 | 5 | public final class EnumHackery { |
17 | 6 |
|
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 | | - |
76 | 7 | public static <T extends Enum<T>> T addEnumEntry(Class<T> enumClass, String enumName) { |
77 | 8 | return addEnumEntry(enumClass, enumName, new Class[0], new Object[0]); |
78 | 9 | } |
79 | 10 |
|
80 | | - @SuppressWarnings("unchecked") |
81 | 11 | 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; |
157 | 15 | } |
158 | 16 |
|
159 | 17 | private EnumHackery() { } |
|
0 commit comments