Skip to content

Instantly share code, notes, and snippets.

@Geolykt
Created May 26, 2021 20:17
Show Gist options
  • Save Geolykt/e8a43f412be7534e1753f57fe072370b to your computer and use it in GitHub Desktop.
Save Geolykt/e8a43f412be7534e1753f57fe072370b to your computer and use it in GitHub Desktop.
/**
* Guesses the inner classes from class nodes
*/
public void fixInnerClasses() {
Map<String, InnerClassNode> splitInner = new HashMap<>();
Set<String> enums = new HashSet<>();
Map<String, List<InnerClassNode>> parents = new HashMap<>();
Map<String, ClassNode> classNodes = new HashMap<>();
// Initial indexing sweep
System.out.println("Inner Classes Fixup: Initial Sweep");
for (ClassNode node : nodes) {
classNodes.put(node.name, node);
parents.put(node.name, new ArrayList<>());
if (node.superName.equals("java/lang/Enum")) {
enums.add(node.name); // Register enum
}
}
// Second sweep
System.out.println("Inner Classes Fixup: Second Sweep");
for (ClassNode node : nodes) {
// Sweep enum members
if (enums.contains(node.superName)) {
// Child of (abstract) enum
boolean skip = false;
for (InnerClassNode innerNode : node.innerClasses) {
if (node.name.equals(innerNode.name)) {
skip = true;
break;
}
}
if (!skip) {
// Apply fixup
// We are using 16400 for access, but are there times where this is not wanted?
InnerClassNode innerNode = new InnerClassNode(node.name, null, null, 16400);
parents.get(node.superName).add(innerNode);
node.innerClasses.add(innerNode);
}
} else if (node.name.contains("$")) {
// This operation cannot be performed during the first sweep
boolean skip = false;
for (InnerClassNode innernode : node.innerClasses) {
if (innernode.name.equals(node.name)) {
skip = true;
break;
}
}
if (!skip) {
int lastSeperator = node.name.lastIndexOf('$');
String outerMost = node.name.substring(0, lastSeperator++);
String parent = outerMost;
String innerMost = node.name.substring(lastSeperator);
if (innerMost.matches("^[0-9]+$")) {
// Anonymous class
outerMost = null;
innerMost = null;
}
// TODO is there a possibility to "recover" the original access of the inner node?
InnerClassNode innerNode = new InnerClassNode(node.name, outerMost, innerMost, node.access);
parents.get(parent).add(innerNode);
splitInner.put(node.name, innerNode);
}
}
}
System.out.println("Inner Classes Fixup: Parent References");
for (ClassNode node : nodes) {
// General sweep
Collection<InnerClassNode> innerNodesToAdd = new ArrayList<>();
for (FieldNode field : node.fields) {
String descriptor = field.desc;
if (descriptor.length() < 4) {
continue; // Most likely a primitive
}
if (descriptor.charAt(0) == '[') {
// Array
descriptor = descriptor.substring(2, descriptor.length() - 1);
} else {
// Non-array
descriptor = descriptor.substring(1, descriptor.length() - 1);
}
InnerClassNode innerNode = splitInner.get(descriptor);
if (innerNode != null) {
if (innerNode.innerName == null/* && !field.name.contains("$")*/) {
// Not fatal, but worrying
System.err.println(String.format("Unlikely field descriptor for field \"%s\" with descriptor %s in class %s", field.name, field.desc, node.name));
}
innerNodesToAdd.add(innerNode);
}
}
// Apply inner nodes
HashSet<String> entryNames = new HashSet<>();
for (InnerClassNode inner : innerNodesToAdd) {
if (entryNames.add(inner.name)) {
node.innerClasses.add(inner);
}
}
}
// Add inner classes to the parent of the anonymous classes
System.out.println("Inner Classes Fixup: Parents");
for (Entry<String, List<InnerClassNode>> entry : parents.entrySet()) {
// Remove duplicates
HashSet<String> entryNames = new HashSet<>();
ArrayList<InnerClassNode> toRemove = new ArrayList<>();
for (InnerClassNode inner : entry.getValue()) {
if (!entryNames.add(inner.name)) {
toRemove.add(inner);
}
}
toRemove.forEach(entry.getValue()::remove);
ClassNode node = classNodes.get(entry.getKey());
for (InnerClassNode innerEntry : entry.getValue()) {
boolean skip = false;
for (InnerClassNode inner : node.innerClasses) {
if (inner.name.equals(innerEntry.name)) {
skip = true;
break;
}
}
if (!skip) {
node.innerClasses.add(innerEntry);
}
}
}
System.out.println("Inner Classes Fixup: Done!");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment