Skip to content

Commit d155dac

Browse files
committed
Merge 1.16 into 1.18
2 parents 5a6ab4f + c6cb0ac commit d155dac

File tree

1 file changed

+53
-181
lines changed

1 file changed

+53
-181
lines changed
Lines changed: 53 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,201 +1,73 @@
11
package org.embeddedt.modernfix.util;
22

3-
import com.google.common.collect.ImmutableMap;
43
import org.embeddedt.modernfix.ModernFix;
54
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
6-
import org.jetbrains.annotations.NotNull;
7-
import org.jetbrains.annotations.Nullable;
5+
import org.objectweb.asm.tree.ClassNode;
6+
import org.spongepowered.asm.mixin.MixinEnvironment;
7+
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
88
import org.spongepowered.asm.mixin.transformer.ClassInfo;
99

1010
import java.lang.reflect.Field;
11-
import java.security.CodeSigner;
12-
import java.util.*;
13-
import java.util.jar.Attributes;
11+
import java.util.Collection;
12+
import java.util.Map;
1413

1514
public class ClassInfoManager {
16-
private static Map<String, ClassInfo> classInfoCache = null;
15+
private static boolean hasRun = false;
1716
public static void clear() {
18-
if(!ModernFixMixinPlugin.instance.isOptionEnabled("perf.clear_mixin_classinfo.ClassInfoManager"))
17+
if (!ModernFixMixinPlugin.instance.isOptionEnabled("perf.clear_mixin_classinfo.ClassInfoManager") || hasRun)
1918
return;
20-
if(classInfoCache == null) {
21-
try {
22-
Field field = ClassInfo.class.getDeclaredField("cache");
23-
field.setAccessible(true);
24-
classInfoCache = (Map<String, ClassInfo>)field.get(null);
25-
} catch(ReflectiveOperationException | RuntimeException e) {
26-
e.printStackTrace();
27-
return;
28-
}
29-
}
30-
try {
31-
classInfoCache.entrySet().removeIf(entry -> !entry.getKey().equals("java/lang/Object") && (entry.getValue() == null || !entry.getValue().isMixin()));
32-
} catch(RuntimeException e) {
33-
e.printStackTrace();
34-
}
35-
36-
// Clear manifest entries
37-
int numManifestsCleared = 0;
38-
// TODO port
39-
/*
40-
for(IModFileInfo mod : ModList.get().getModFiles()) {
41-
Manifest manifest = mod.getFile().getSecureJar().getManifest();
42-
if(manifest.getEntries() instanceof HashMap<String, Attributes> entryMap) {
43-
for (Map.Entry<String, Attributes> entry : entryMap.entrySet()) {
44-
Attributes attributes = entry.getValue();
45-
if (attributes.size() == 1 && attributes.getValue("SHA-256-Digest") != null) {
46-
try {
47-
entry.setValue(EmptyAttributes.INSTANCE);
48-
numManifestsCleared++;
49-
} catch (RuntimeException ignored) {
50-
}
51-
}
52-
}
53-
}
54-
}
55-
56-
*/
57-
if(numManifestsCleared > 0)
58-
ModernFix.LOGGER.info("Cleared {} manifest attributes", numManifestsCleared);
59-
60-
try {
61-
clearSecureJarStructs();
62-
} catch(Throwable e) {
63-
ModernFix.LOGGER.error("Couldn't clear Jar structs", e);
64-
}
65-
19+
hasRun = true;
20+
ModernFix.resourceReloadExecutor().execute(ClassInfoManager::doClear);
6621
}
6722

68-
private static void clearSecureJarStructs() throws Throwable {
69-
/*
70-
// Clear Jar signing data
71-
Unsafe unsafe;
72-
Field f = Unsafe.class.getDeclaredField("theUnsafe");
23+
private static Field accessible(Field f) {
7324
f.setAccessible(true);
74-
unsafe = (Unsafe)f.get(null);
75-
76-
Field statusDataField, pendingSignersField, verifiedSignersField;
77-
statusDataField = Jar.class.getDeclaredField("statusData");
78-
pendingSignersField = Jar.class.getDeclaredField("pendingSigners");
79-
verifiedSignersField = Jar.class.getDeclaredField("verifiedSigners");
80-
81-
long statusDataOffset = unsafe.objectFieldOffset(statusDataField);
82-
long pendingSignersOffset = unsafe.objectFieldOffset(pendingSignersField);
83-
long verifiedSignersOffset = unsafe.objectFieldOffset(verifiedSignersField);
84-
85-
for(IModFileInfo mod : ModList.get().getModFiles()) {
86-
SecureJar secureJar = mod.getFile().getSecureJar();
87-
if(secureJar instanceof Jar) {
88-
unsafe.putObject(secureJar, statusDataOffset, LyingStatusDataMap.INSTANCE);
89-
unsafe.putObject(secureJar, pendingSignersOffset, EmptyCodeSignerTable.INSTANCE);
90-
unsafe.putObject(secureJar, verifiedSignersOffset, EmptyCodeSignerTable.INSTANCE);
91-
}
92-
}
93-
94-
*/
25+
return f;
9526
}
9627

97-
static class EmptyCodeSignerTable extends Hashtable<String, CodeSigner[]> {
98-
public static final EmptyCodeSignerTable INSTANCE = new EmptyCodeSignerTable();
99-
private static final CodeSigner[] VAL = new CodeSigner[0];
100-
101-
@Override
102-
public synchronized CodeSigner[] put(String key, CodeSigner[] value) {
103-
return null;
104-
}
105-
106-
@Override
107-
public synchronized boolean isEmpty() {
108-
return true;
109-
}
110-
111-
@Override
112-
public synchronized boolean containsKey(Object key) {
113-
return false;
114-
}
115-
116-
@Override
117-
public synchronized CodeSigner[] get(Object key) {
118-
return VAL;
119-
}
120-
}
121-
122-
/**
123-
* This map is used to replace the statusData map.
124-
*
125-
* The lying in containsKey is intentionally done to force certain code paths to run in Jar.
126-
* Otherwise the security information might be recomputed many times.
127-
*/
128-
static class LyingStatusDataMap implements Map<String, Object> {
129-
public static final LyingStatusDataMap INSTANCE = new LyingStatusDataMap();
130-
@Override
131-
public int size() {
132-
return 0;
133-
}
134-
135-
@Override
136-
public boolean isEmpty() {
137-
return false;
138-
}
139-
140-
@Override
141-
public boolean containsKey(Object o) {
142-
return true;
143-
}
144-
145-
@Override
146-
public boolean containsValue(Object o) {
147-
return false;
148-
}
149-
150-
@Override
151-
public Object get(Object o) {
152-
return null;
153-
}
154-
155-
@Nullable
156-
@Override
157-
public Object put(String s, Object o) {
158-
return null;
159-
}
160-
161-
@Override
162-
public Object remove(Object o) {
163-
return null;
164-
}
165-
166-
@Override
167-
public void putAll(@NotNull Map<? extends String, ?> map) {
168-
169-
}
170-
171-
@Override
172-
public void clear() {
173-
}
174-
175-
@NotNull
176-
@Override
177-
public Set<String> keySet() {
178-
return Collections.emptySet();
179-
}
180-
181-
@NotNull
182-
@Override
183-
public Collection<Object> values() {
184-
return Collections.emptyList();
185-
}
186-
187-
@NotNull
188-
@Override
189-
public Set<Entry<String, Object>> entrySet() {
190-
return Collections.emptySet();
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();
43+
return;
19144
}
192-
}
193-
194-
static class EmptyAttributes extends Attributes {
195-
public static final EmptyAttributes INSTANCE = new EmptyAttributes();
196-
EmptyAttributes() {
197-
super(1);
198-
this.map = ImmutableMap.of();
45+
MixinEnvironment.getDefaultEnvironment().audit();
46+
try {
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) {
69+
e.printStackTrace();
19970
}
71+
ModernFix.LOGGER.warn("Cleared mixin data structures");
20072
}
20173
}

0 commit comments

Comments
 (0)