From 9fbbd9ada97e306da7e893803e4f67efdce5818b Mon Sep 17 00:00:00 2001 From: Algent Date: Mon, 14 Sep 2026 13:36:26 +0200 Subject: [PATCH] chore: remove no longer needed temp tool --- tools/AbiScan.java | 233 --------------------------------------------- 1 file changed, 233 deletions(-) delete mode 100644 tools/AbiScan.java diff --git a/tools/AbiScan.java b/tools/AbiScan.java deleted file mode 100644 index 59d0184..0000000 --- a/tools/AbiScan.java +++ /dev/null @@ -1,233 +0,0 @@ -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * Scans a directory of mod jars for constant-pool references into ForgeMultipart's packages. - * - * Usage: java AbiScan.java [selfJarNameFragment...] - * - * Reports, per consumer jar: inherited types, referenced members with exact JVM descriptors, - * other referenced types, and string constants that look like reflective lookups. - */ -public final class AbiScan { - - private static final String[] TARGETS = { "codechicken/multipart", "codechicken/microblock" }; - - /** owner.name descriptor -> jars that reference it. */ - private static final Map> MEMBERS = new TreeMap<>(); - /** type -> jars that extend or implement it. */ - private static final Map> INHERITED = new TreeMap<>(); - /** type -> jars that reference it any other way. */ - private static final Map> TYPES = new TreeMap<>(); - /** string constant -> jars that embed it. */ - private static final Map> STRINGS = new TreeMap<>(); - /** jar -> count of classes touching the targets. */ - private static final Map HITS = new TreeMap<>(); - - public static void main(String[] args) throws IOException { - Path modsDir = Paths.get(args[0]); - List self = new ArrayList<>(); - for (int i = 1; i < args.length; i++) { - self.add(args[i].toLowerCase()); - } - - List jars; - try (var stream = Files.walk(modsDir)) { - jars = stream.filter(p -> p.toString().toLowerCase().endsWith(".jar")).sorted().collect(Collectors.toList()); - } - - int skipped = 0; - for (Path jar : jars) { - String name = jar.getFileName().toString(); - if (self.stream().anyMatch(name.toLowerCase()::contains)) { - skipped++; - continue; - } - scanJar(jar, name); - } - - System.out.println("Scanned " + (jars.size() - skipped) + " jars (" + skipped + " excluded as ForgeMultipart itself)"); - System.out.println(); - - System.out.println("== CONSUMERS =="); - HITS.entrySet().stream() - .sorted(Map.Entry.comparingByValue().reversed()) - .forEach(e -> System.out.println(" " + e.getValue() + "\t" + e.getKey())); - System.out.println(); - - dump("INHERITED TYPES (downstream extends/implements - abstract method descriptors are load-bearing)", INHERITED); - dump("REFERENCED MEMBERS (exact descriptors that must keep linking)", MEMBERS); - dump("OTHER REFERENCED TYPES (new/cast/instanceof/field descriptors)", TYPES); - dump("STRING CONSTANTS (reflection / registration candidates)", STRINGS); - } - - private static void dump(String title, Map> data) { - System.out.println("== " + title + " == (" + data.size() + ")"); - data.entrySet().stream() - .sorted(Comparator.>>comparingInt(e -> -e.getValue().size()) - .thenComparing(Map.Entry::getKey)) - .forEach(e -> System.out.println(" " + e.getKey() + "\n <- " + String.join(", ", e.getValue()))); - System.out.println(); - } - - private static void scanJar(Path jar, String jarName) { - try (ZipFile zip = new ZipFile(jar.toFile())) { - var entries = zip.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - if (!entry.getName().endsWith(".class")) { - continue; - } - try (InputStream in = zip.getInputStream(entry)) { - scanClass(new DataInputStream(in), jarName); - } catch (Exception ignored) { - // Unparseable or non-standard class file; not evidence of a reference. - } - } - } catch (IOException ignored) { - System.err.println("skip unreadable jar: " + jarName); - } - } - - private static void scanClass(DataInputStream in, String jarName) throws IOException { - if (in.readInt() != 0xCAFEBABE) { - return; - } - in.readUnsignedShort(); // minor - in.readUnsignedShort(); // major - int count = in.readUnsignedShort(); - - int[] tags = new int[count]; - String[] utf8 = new String[count]; - int[] refA = new int[count]; - int[] refB = new int[count]; - - for (int i = 1; i < count; i++) { - int tag = in.readUnsignedByte(); - tags[i] = tag; - switch (tag) { - case 1 -> utf8[i] = in.readUTF(); - case 7, 8, 16, 19, 20 -> refA[i] = in.readUnsignedShort(); - case 15 -> { in.readUnsignedByte(); refA[i] = in.readUnsignedShort(); } - case 3, 4 -> in.readInt(); - case 5, 6 -> { in.readLong(); i++; } - case 9, 10, 11, 12, 17, 18 -> { refA[i] = in.readUnsignedShort(); refB[i] = in.readUnsignedShort(); } - default -> throw new IOException("bad constant pool tag " + tag); - } - } - - in.readUnsignedShort(); // access_flags - int thisClass = in.readUnsignedShort(); - int superClass = in.readUnsignedShort(); - int interfaceCount = in.readUnsignedShort(); - int[] interfaces = new int[interfaceCount]; - for (int i = 0; i < interfaceCount; i++) { - interfaces[i] = in.readUnsignedShort(); - } - - String owner = className(tags, utf8, refA, thisClass); - if (owner != null && isTarget(owner)) { - return; // a repackaged copy of ForgeMultipart, not a consumer - } - - boolean hit = false; - Set inheritedHere = new TreeSet<>(); - String superName = className(tags, utf8, refA, superClass); - if (superName != null && isTarget(superName)) { - inheritedHere.add(superName); - } - for (int itf : interfaces) { - String name = className(tags, utf8, refA, itf); - if (name != null && isTarget(name)) { - inheritedHere.add(name); - } - } - for (String type : inheritedHere) { - INHERITED.computeIfAbsent(type, k -> new TreeSet<>()).add(jarName); - hit = true; - } - - for (int i = 1; i < count; i++) { - switch (tags[i]) { - case 7 -> { - String name = utf8[refA[i]]; - if (name != null && isTarget(name) && !inheritedHere.contains(strip(name))) { - TYPES.computeIfAbsent(strip(name), k -> new TreeSet<>()).add(jarName); - hit = true; - } - } - case 9, 10, 11 -> { - String ownerName = className(tags, utf8, refA, refA[i]); - if (ownerName == null || !isTarget(ownerName)) { - break; - } - int nat = refB[i]; - String member = utf8[refA[nat]] + " " + utf8[refB[nat]]; - MEMBERS.computeIfAbsent(ownerName + "." + member, k -> new TreeSet<>()).add(jarName); - hit = true; - } - case 8 -> { - String value = utf8[refA[i]]; - if (value != null && (value.contains("codechicken.multipart") || value.contains("codechicken.microblock") - || value.contains("codechicken/multipart") || value.contains("codechicken/microblock"))) { - STRINGS.computeIfAbsent(value, k -> new TreeSet<>()).add(jarName); - hit = true; - } - } - default -> { } - } - } - - if (hit) { - HITS.merge(jarName, 1, Integer::sum); - } - } - - private static String className(int[] tags, String[] utf8, int[] refA, int index) { - if (index <= 0 || index >= tags.length || tags[index] != 7) { - return null; - } - String name = utf8[refA[index]]; - return name == null ? null : strip(name); - } - - /** Array class references are encoded as descriptors; reduce them to the element type. */ - private static String strip(String name) { - int i = 0; - while (i < name.length() && name.charAt(i) == '[') { - i++; - } - if (i == 0) { - return name; - } - String rest = name.substring(i); - if (rest.startsWith("L") && rest.endsWith(";")) { - rest = rest.substring(1, rest.length() - 1); - } - return rest; - } - - private static boolean isTarget(String name) { - String stripped = strip(name); - for (String target : TARGETS) { - if (stripped.startsWith(target + "/")) { - return true; - } - } - return false; - } -}