|
1 | 1 | package org.embeddedt.modernfix.util; |
2 | 2 |
|
| 3 | +import org.embeddedt.modernfix.ModernFix; |
3 | 4 | import org.embeddedt.modernfix.core.ModernFixMixinPlugin; |
| 5 | +import org.objectweb.asm.tree.ClassNode; |
| 6 | +import org.spongepowered.asm.mixin.MixinEnvironment; |
| 7 | +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; |
4 | 8 | import org.spongepowered.asm.mixin.transformer.ClassInfo; |
5 | 9 |
|
6 | 10 | import java.lang.reflect.Field; |
| 11 | +import java.util.Collection; |
7 | 12 | import java.util.Map; |
8 | 13 |
|
9 | 14 | public class ClassInfoManager { |
10 | | - private static Map<String, ClassInfo> classInfoCache = null; |
| 15 | + private static boolean hasRun = false; |
11 | 16 | public static void clear() { |
12 | | - if(!ModernFixMixinPlugin.instance.isOptionEnabled("perf.clear_mixin_classinfo.ClassInfoManager")) |
| 17 | + if (!ModernFixMixinPlugin.instance.isOptionEnabled("perf.clear_mixin_classinfo.ClassInfoManager") || hasRun) |
| 18 | + return; |
| 19 | + hasRun = true; |
| 20 | + ModernFix.resourceReloadExecutor().execute(ClassInfoManager::doClear); |
| 21 | + } |
| 22 | + |
| 23 | + private static Field accessible(Field f) { |
| 24 | + f.setAccessible(true); |
| 25 | + return f; |
| 26 | + } |
| 27 | + |
| 28 | + private static void doClear() { |
| 29 | + Map<String, ClassInfo> classInfoCache; |
| 30 | + Field mixinField, stateField, classNodeField, methodsField, fieldsField; |
| 31 | + Class<?> stateClz; |
| 32 | + try { |
| 33 | + Field field = accessible(ClassInfo.class.getDeclaredField("cache")); |
| 34 | + classInfoCache = (Map<String, ClassInfo>) field.get(null); |
| 35 | + mixinField = accessible(ClassInfo.class.getDeclaredField("mixin")); |
| 36 | + methodsField = accessible(ClassInfo.class.getDeclaredField("methods")); |
| 37 | + fieldsField = accessible(ClassInfo.class.getDeclaredField("fields")); |
| 38 | + stateClz = Class.forName("org.spongepowered.asm.mixin.transformer.MixinInfo$State"); |
| 39 | + stateField = accessible(Class.forName("org.spongepowered.asm.mixin.transformer.MixinInfo").getDeclaredField("state")); |
| 40 | + classNodeField = accessible(stateClz.getDeclaredField("classNode")); |
| 41 | + } catch (ReflectiveOperationException | RuntimeException e) { |
| 42 | + e.printStackTrace(); |
13 | 43 | return; |
14 | | - if(classInfoCache == null) { |
15 | | - try { |
16 | | - Field field = ClassInfo.class.getDeclaredField("cache"); |
17 | | - field.setAccessible(true); |
18 | | - classInfoCache = (Map<String, ClassInfo>)field.get(null); |
19 | | - } catch(ReflectiveOperationException | RuntimeException e) { |
20 | | - e.printStackTrace(); |
21 | | - return; |
22 | | - } |
23 | 44 | } |
| 45 | + MixinEnvironment.getDefaultEnvironment().audit(); |
24 | 46 | try { |
25 | | - classInfoCache.entrySet().removeIf(entry -> !entry.getKey().equals("java/lang/Object") && (entry.getValue() == null || !entry.getValue().isMixin())); |
26 | | - } catch(RuntimeException e) { |
| 47 | + ClassNode emptyNode = new ClassNode(); |
| 48 | + classInfoCache.entrySet().removeIf(entry -> { |
| 49 | + if(entry.getKey().equals("java/lang/Object")) |
| 50 | + return false; |
| 51 | + ClassInfo mixinClz = entry.getValue(); |
| 52 | + try { |
| 53 | + if(mixinClz.isMixin()) { |
| 54 | + // clear classNode in MixinInfo.State |
| 55 | + IMixinInfo theInfo = (IMixinInfo) mixinField.get(mixinClz); |
| 56 | + Object state = stateField.get(theInfo); |
| 57 | + if (state != null) |
| 58 | + classNodeField.set(state, emptyNode); |
| 59 | + } |
| 60 | + // clear fields, methods |
| 61 | + ((Collection<?>)methodsField.get(mixinClz)).clear(); |
| 62 | + ((Collection<?>)fieldsField.get(mixinClz)).clear(); |
| 63 | + } catch (ReflectiveOperationException | RuntimeException e) { |
| 64 | + e.printStackTrace(); |
| 65 | + } |
| 66 | + return true; |
| 67 | + }); |
| 68 | + } catch (RuntimeException e) { |
27 | 69 | e.printStackTrace(); |
28 | 70 | } |
| 71 | + ModernFix.LOGGER.warn("Cleared mixin data structures"); |
29 | 72 | } |
30 | 73 | } |
0 commit comments