Skip to content

Commit

Permalink
prune equivalent class files
Browse files Browse the repository at this point in the history
  • Loading branch information
wagyourtail committed Jul 31, 2024
1 parent 7e86cb6 commit 6a59f14
Showing 1 changed file with 71 additions and 1 deletion.
72 changes: 71 additions & 1 deletion src/main/java/xyz/wagyourtail/jvmdg/ClassDowngrader.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ public List<VersionProvider> versionProviders(int inputVersion) {
public Map<String, byte[]> downgrade(/* in out */ AtomicReference<String> name, @NotNull byte[] bytes, boolean enableRuntime, final Function<String, byte[]> getExtraRead) throws IllegalClassFormatException {
// check magic
if (bytes[0] != (byte) 0xCA || bytes[1] != (byte) 0xFE || bytes[2] != (byte) 0xBA ||
bytes[3] != (byte) 0xBE) {
bytes[3] != (byte) 0xBE) {
throw new IllegalClassFormatException(name.get());
}
// ignore minor version
Expand Down Expand Up @@ -317,6 +317,7 @@ public ClassNode apply(String s) {
}
}
});
boolean hasVersions = false;
for (ClassNode c : extra) {
// TODO: uncomment with asm 9.8
// if (flags.debugDumpClasses) {
Expand All @@ -332,10 +333,58 @@ public ClassNode apply(String s) {
if (c.version > target) {
// write to multi-release location
outputs.put("META-INF/versions/" + Utils.classVersionToMajorVersion(c.version) + "/" + c.name, cBytes);
hasVersions = true;
} else {
outputs.put(c.name, cBytes);
}
}
if (hasVersions) {
// filter outputs that are effectively the same
Map<String, Map<Integer, byte[]>> byVersion = new HashMap<>();
for (Map.Entry<String, byte[]> entry : outputs.entrySet()) {
String key = entry.getKey();
if (key.startsWith("META-INF/versions/")) {
int major = Integer.parseInt(key.substring(18, key.indexOf('/', 18)));
String cName = key.substring(key.indexOf('/', 18) + 1);
if (!byVersion.containsKey(cName)) {
byVersion.put(cName, new TreeMap<Integer, byte[]>());
}
byVersion.get(cName).put(major, entry.getValue());
} else {
if (!byVersion.containsKey(key)) {
byVersion.put(key, new TreeMap<Integer, byte[]>());
}
byVersion.get(key).put(0, entry.getValue());
}
}
outputs.clear();
Map<String, byte[]> current = new HashMap<>();
for (Map.Entry<String, Map<Integer, byte[]>> entry : byVersion.entrySet()) {
String key = entry.getKey();
Map<Integer, byte[]> versions = entry.getValue();
if (!versions.containsKey(0)) {
throw new IllegalStateException("Found multi-release class " + key + " without main version!");
}
if (versions.size() == 1) {
outputs.put(key, versions.get(0));
}
// order will be in numeric increasing order due to how TreeMap works :)
for (Map.Entry<Integer, byte[]> vs : versions.entrySet()) {
if (vs.getKey() == 0) {
current.put(key, vs.getValue());
outputs.put(key, vs.getValue());
} else {
byte[] currentVal = current.get(key);
byte[] vsVal = vs.getValue();
// equal after version info
if (!equals(currentVal, 8, currentVal.length, vsVal, 8, vsVal.length)) {
current.put(key, vsVal);
outputs.put("META-INF/versions/" + vs.getKey() + "/" + key, vsVal);
}
}
}
}
}
} catch (Exception e) {
throw new RuntimeException("Failed to downgrade " + name.get(), e);
}
Expand All @@ -354,6 +403,27 @@ public ClassNode apply(String s) {
return outputs;
}

public static boolean equals(
byte[] a, int aFromIndex, int aToIndex,
byte[] b, int bFromIndex, int bToIndex
) {
int aLength = aToIndex - aFromIndex;
int bLength = bToIndex - bFromIndex;
if (aLength != bLength) {
return false;
}
if (aLength == 0) {
return true;
}
for (int i = 0; i < aLength; i++) {
if (a[aFromIndex + i] != b[bFromIndex + i]) {
return false;
}
}
return true;
}


public byte[] classNodeToBytes(@NotNull final ClassNode node) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
node.accept(cw);
Expand Down

0 comments on commit 6a59f14

Please sign in to comment.