Skip to content

Instantly share code, notes, and snippets.

@Runemoro
Last active March 18, 2019 20:58
Show Gist options
  • Save Runemoro/9ef4d6a03d293c4ea6d399a559ffcc85 to your computer and use it in GitHub Desktop.
Save Runemoro/9ef4d6a03d293c4ea6d399a559ffcc85 to your computer and use it in GitHub Desktop.
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