Last active
March 18, 2019 20:58
-
-
Save Runemoro/9ef4d6a03d293c4ea6d399a559ffcc85 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cuchaz.enigma.analysis.ParsedJar; | |
import cuchaz.enigma.analysis.index.BridgeMethodIndex; | |
import cuchaz.enigma.analysis.index.JarIndex; | |
import net.fabricmc.mappings.EntryTriple; | |
import net.fabricmc.mappings.Mappings; | |
import net.fabricmc.mappings.MappingsProvider; | |
import net.fabricmc.mappings.MethodEntry; | |
import org.objectweb.asm.ClassReader; | |
import org.objectweb.asm.ClassVisitor; | |
import org.objectweb.asm.MethodVisitor; | |
import org.objectweb.asm.Opcodes; | |
import java.io.FileInputStream; | |
import java.io.InputStream; | |
import java.lang.reflect.Field; | |
import java.util.*; | |
import java.util.jar.JarEntry; | |
import java.util.jar.JarFile; | |
public class YarnStatistics { | |
public static void main(String[] args) throws Throwable { | |
Set<String> syntheticMethods = new HashSet<>(); | |
try (JarFile jarFile = new JarFile("19w11b.jar")) { | |
Enumeration<JarEntry> entries = jarFile.entries(); | |
while (entries.hasMoreElements()) { | |
JarEntry entry = entries.nextElement(); | |
if (!entry.getName().endsWith(".class")) { | |
continue; | |
} | |
String className = entry.getName().substring(0, entry.getName().length() - 6); | |
new ClassReader(jarFile.getInputStream(entry)).accept(new ClassVisitor(Opcodes.ASM7) { | |
@Override | |
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { | |
if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
syntheticMethods.add(className + ":" + name + descriptor); | |
} | |
return super.visitMethod(access, name, descriptor, signature, exceptions); | |
} | |
}, 0); | |
} | |
} | |
Map<String, String> accessedToBridge = new HashMap<>(); | |
try (JarFile jarFile = new JarFile("19w11b.jar")) { | |
JarIndex jarIndex = JarIndex.empty(); | |
jarIndex.indexJar(new ParsedJar(jarFile), System.out::println); | |
Field accessedToBridgeField = BridgeMethodIndex.class.getDeclaredField("accessedToBridge"); | |
accessedToBridgeField.setAccessible(true); | |
Map<cuchaz.enigma.translation.representation.entry.MethodEntry, cuchaz.enigma.translation.representation.entry.MethodEntry> accessedToBridge2 = (Map<cuchaz.enigma.translation.representation.entry.MethodEntry, cuchaz.enigma.translation.representation.entry.MethodEntry>) accessedToBridgeField.get(jarIndex.getBridgeMethodIndex()); | |
accessedToBridge2.forEach((accessed, bridge) -> accessedToBridge.put( | |
accessed.getContainingClass().getFullName() + ":" + accessed.getName() + accessed.getDesc(), | |
bridge.getContainingClass().getFullName() + ":" + bridge.getName() + bridge.getDesc() | |
)); | |
} | |
Mappings mappings; | |
try (InputStream stream = new FileInputStream("mappings.tiny")) { | |
mappings = MappingsProvider.readTinyMappings(stream); | |
} | |
Map<String, Integer> unmappedCount = new LinkedHashMap<>(); | |
Map<String, Integer> mappedCount = new LinkedHashMap<>(); | |
Set<String> seenNames = new HashSet<>(); | |
for (MethodEntry methodEntry : mappings.getMethodEntries()) { | |
EntryTriple intermediary = methodEntry.get("intermediary"); | |
if (!seenNames.add(intermediary.getName())) { | |
continue; | |
} | |
EntryTriple original = methodEntry.get("official"); | |
String key = original.getOwner() + ":" + original.getName() + original.getDesc(); | |
if (accessedToBridge.containsKey(key)) { | |
continue; | |
} | |
if (syntheticMethods.contains(key)) { | |
continue; | |
} | |
EntryTriple named = methodEntry.get("named"); | |
boolean mapped = !named.getName().equals(intermediary.getName()); | |
StringBuilder packageName = new StringBuilder(); | |
for (String part : named.getOwner().split("/")) { | |
if (mapped) { | |
mappedCount.put(packageName.toString(), mappedCount.getOrDefault(packageName.toString(), 0) + 1); | |
} else { | |
unmappedCount.put(packageName.toString(), unmappedCount.getOrDefault(packageName.toString(), 0) + 1); | |
} | |
packageName.append("/").append(part); | |
} | |
} | |
unmappedCount.remove("/net"); | |
unmappedCount.remove(""); | |
String[] last = {""}; | |
unmappedCount | |
.keySet() | |
.stream() | |
.sorted() | |
.forEachOrdered(packageName -> { | |
int prefixSize = findCommonPrefixSize(last[0].split("/"), packageName.split("/")); | |
StringBuilder prefix = new StringBuilder(); | |
for (int i = 1; i < countCharacters(packageName.substring(0, prefixSize), '/'); i++) { | |
prefix.append(" "); | |
} | |
String suffix = packageName.substring(prefixSize == 0 ? 0 : prefixSize + 1); | |
if (suffix.isEmpty()) { | |
suffix = "(none)"; | |
} | |
prefix.append(suffix); | |
int mapped = mappedCount.getOrDefault(packageName, 0); | |
int unmapped = unmappedCount.getOrDefault(packageName, 0); | |
int total = unmapped + mapped; | |
int l = prefix.length(); | |
for (int i = 0; i < 25 - l; i++) { | |
prefix.append(" "); | |
} | |
System.out.printf(prefix + " %.2f%% (%d/%d) mapped - %d unmapped\n", (double) mapped / total * 100, mapped, total, unmapped); | |
last[0] = packageName; | |
}); | |
} | |
private static int countCharacters(String s, char c) { | |
int n = 0; | |
for (int i = 0; i < s.length(); i++) { | |
if (s.charAt(i) == c) { | |
n++; | |
} | |
} | |
return n; | |
} | |
private static int findCommonPrefixSize(String[] a, String[] b) { | |
int shortestLength = Math.min(a.length, b.length); | |
int size = 0; | |
for (int i = 0; i < shortestLength; i++) { | |
if (!a[i].equals(b[i])) { | |
return size - 1; | |
} | |
size += a[i].length() + 1; | |
} | |
return size - 1; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment