Skip to content

Commit

Permalink
rework handling of inner classes
Browse files Browse the repository at this point in the history
  • Loading branch information
SpaceWalkerRS committed May 8, 2023
1 parent 0abaedb commit 2d3887d
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 179 deletions.
59 changes: 42 additions & 17 deletions src/main/java/net/fabricmc/stitch/commands/GenState.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ public void generate(File file, JarRootEntry jarEntry, JarRootEntry jarOld) thro
}

public static boolean isMappedClass(ClassStorage storage, JarClassEntry c) {
return !c.isAnonymous();
// if an anonymous class' name does not follow the $ convention, we give it a new name anyway
return !(c.isAnonymous() && c.getName().startsWith(c.getEnclosingClassName() + "$"));
}

public static boolean isMappedField(ClassStorage storage, JarClassEntry c, JarFieldEntry f) {
Expand All @@ -143,7 +144,7 @@ private String getFieldName(ClassStorage storage, JarClassEntry c, JarFieldEntry
}

if (newToIntermediary != null) {
EntryTriple findEntry = newToIntermediary.getField(c.getFullyQualifiedName(), f.getName(), f.getDescriptor());
EntryTriple findEntry = newToIntermediary.getField(c.getName(), f.getName(), f.getDescriptor());
if (findEntry != null) {
if (findEntry.getName().contains("field_")) {
return findEntry.getName();
Expand All @@ -156,7 +157,7 @@ private String getFieldName(ClassStorage storage, JarClassEntry c, JarFieldEntry
}

if (newToOld != null) {
EntryTriple findEntry = newToOld.getField(c.getFullyQualifiedName(), f.getName(), f.getDescriptor());
EntryTriple findEntry = newToOld.getField(c.getName(), f.getName(), f.getDescriptor());
if (findEntry != null) {
findEntry = oldToIntermediary.getField(findEntry);
if (findEntry != null) {
Expand All @@ -181,7 +182,7 @@ private String getPropagation(ClassStorage storage, JarClassEntry classEntry) {
return "";
}

StringBuilder builder = new StringBuilder(classEntry.getFullyQualifiedName());
StringBuilder builder = new StringBuilder(classEntry.getName());
List<String> strings = new ArrayList<>();
String scs = getPropagation(storage, classEntry.getSuperClass(storage));
if (!scs.isEmpty()) {
Expand Down Expand Up @@ -240,28 +241,28 @@ private void findNames(ClassStorage storageOld, ClassStorage storageNew, JarClas
for (JarClassEntry cc : ccList) {
EntryTriple findEntry = null;
if (newToIntermediary != null) {
findEntry = newToIntermediary.getMethod(cc.getFullyQualifiedName(), m.getName(), m.getDescriptor());
findEntry = newToIntermediary.getMethod(cc.getName(), m.getName(), m.getDescriptor());
if (findEntry != null) {
names.computeIfAbsent(findEntry.getName(), (s) -> new TreeSet<>()).add(getNamesListEntry(storageNew, cc) + suffix);
}
}

if (findEntry == null && newToOld != null) {
findEntry = newToOld.getMethod(cc.getFullyQualifiedName(), m.getName(), m.getDescriptor());
findEntry = newToOld.getMethod(cc.getName(), m.getName(), m.getDescriptor());
if (findEntry != null) {
EntryTriple newToOldEntry = findEntry;
findEntry = oldToIntermediary.getMethod(newToOldEntry);
if (findEntry != null) {
names.computeIfAbsent(findEntry.getName(), (s) -> new TreeSet<>()).add(getNamesListEntry(storageNew, cc) + suffix);
} else {
// more involved...
JarClassEntry oldBase = storageOld.getClass(newToOldEntry.getOwner(), false);
JarClassEntry oldBase = storageOld.getClass(newToOldEntry.getOwner(), null);
if (oldBase != null) {
JarMethodEntry oldM = oldBase.getMethod(newToOldEntry.getName() + newToOldEntry.getDesc());
List<JarClassEntry> cccList = oldM.getMatchingEntries(storageOld, oldBase);

for (JarClassEntry ccc : cccList) {
findEntry = oldToIntermediary.getMethod(ccc.getFullyQualifiedName(), oldM.getName(), oldM.getDescriptor());
findEntry = oldToIntermediary.getMethod(ccc.getName(), oldM.getName(), oldM.getDescriptor());
if (findEntry != null) {
names.computeIfAbsent(findEntry.getName(), (s) -> new TreeSet<>()).add(getNamesListEntry(storageOld, ccc) + suffix);
}
Expand Down Expand Up @@ -349,21 +350,44 @@ private String getMethodName(ClassStorage storageOld, ClassStorage storageNew, J
}

private void addClass(BufferedWriter writer, JarClassEntry c, ClassStorage storageOld, ClassStorage storage, String translatedPrefix) throws IOException {
String className = c.getName();
String fullName = c.getName();
// Typically inner class names are of the form com/example/Example$InnerName
// but this is not required by the JVM spec! However, for the names we generate
// we do follow this convention.
if (c.isLocal()) {
String enclName = c.getEnclosingClassName();
String innerName = c.getInnerName();
// typically, local classes' full names have a number prefix
// before the inner name part - this is useful for allowing
// multiple local classes to have the same inner name, so we add it
if (fullName.startsWith(enclName + "$") && fullName.endsWith(innerName)) {
translatedPrefix += fullName.substring(enclName.length() + 1, fullName.length() - innerName.length());
}
}
String cname = "";
String prefixSaved = translatedPrefix;

if(!this.obfuscatedPatterns.stream().anyMatch(p -> p.matcher(className).matches())) {
translatedPrefix = c.getFullyQualifiedName();
if(!this.obfuscatedPatterns.stream().anyMatch(p -> p.matcher(fullName).matches())) {
translatedPrefix = fullName;
} else {
if (!isMappedClass(storage, c)) {
cname = c.getName();
if (c.isAnonymous()) {
// anonymous classes are only unmapped if their name
// follows the standard $ convention
cname = fullName.substring(c.getEnclosingClassName().length() + 1);
} else {
// throw exception in case the impl of isMappedClass
// changes but we forget to deal with it here
throw new IllegalStateException("don't know what to do with class " + fullName);
}
} else {
cname = null;

if (newToIntermediary != null) {
String findName = newToIntermediary.getClass(c.getFullyQualifiedName());
String findName = newToIntermediary.getClass(fullName);
if (findName != null) {
// the names we generate follow the $ convention for inner class names,
// so we can safely find the inner name like this
String[] r = findName.split("\\$");
cname = r[r.length - 1];
if (r.length == 1) {
Expand All @@ -373,10 +397,11 @@ private void addClass(BufferedWriter writer, JarClassEntry c, ClassStorage stora
}

if (cname == null && newToOld != null) {
String findName = newToOld.getClass(c.getFullyQualifiedName());
String findName = newToOld.getClass(fullName);
if (findName != null) {
findName = oldToIntermediary.getClass(findName);
if (findName != null) {
// for the same reason as above, we can safely find the inner name like this
String[] r = findName.split("\\$");
cname = r[r.length - 1];
if (r.length == 1) {
Expand All @@ -400,7 +425,7 @@ private void addClass(BufferedWriter writer, JarClassEntry c, ClassStorage stora
}
}

writer.write("CLASS\t" + c.getFullyQualifiedName() + "\t" + translatedPrefix + cname + "\n");
writer.write("CLASS\t" + fullName + "\t" + translatedPrefix + cname + "\n");

for (JarFieldEntry f : c.getFields()) {
String fName = getFieldName(storage, c, f);
Expand All @@ -409,7 +434,7 @@ private void addClass(BufferedWriter writer, JarClassEntry c, ClassStorage stora
}

if (fName != null) {
writer.write("FIELD\t" + c.getFullyQualifiedName()
writer.write("FIELD\t" + fullName
+ "\t" + f.getDescriptor()
+ "\t" + f.getName()
+ "\t" + fName + "\n");
Expand All @@ -425,7 +450,7 @@ private void addClass(BufferedWriter writer, JarClassEntry c, ClassStorage stora
}

if (mName != null) {
writer.write("METHOD\t" + c.getFullyQualifiedName()
writer.write("METHOD\t" + fullName
+ "\t" + m.getDescriptor()
+ "\t" + m.getName()
+ "\t" + mName + "\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
package net.fabricmc.stitch.representation;

public interface ClassStorage {
JarClassEntry getClass(String name, boolean create);
JarClassEntry getClass(String name, JarClassEntry.Populator populator);
}
133 changes: 107 additions & 26 deletions src/main/java/net/fabricmc/stitch/representation/JarClassEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.stream.Collectors;

public class JarClassEntry extends AbstractJarEntry {
String fullyQualifiedName;
final Map<String, JarClassEntry> innerClasses;
final Map<String, JarFieldEntry> fields;
final Map<String, JarMethodEntry> methods;
Expand All @@ -34,11 +33,17 @@ public class JarClassEntry extends AbstractJarEntry {
List<String> interfaces;
List<String> subclasses;
List<String> implementers;

protected JarClassEntry(String name, String fullyQualifiedName) {
/** outer class for inner classes */
String declaringClass;
/** outer class for anonymous and local classes */
String enclosingClass;
String enclosingMethod;
String enclosingMethodDesc;
String innerName;

protected JarClassEntry(String name) {
super(name);

this.fullyQualifiedName = fullyQualifiedName;
this.innerClasses = new TreeMap<>(Comparator.naturalOrder());
this.fields = new TreeMap<>(Comparator.naturalOrder());
this.methods = new TreeMap<>(Comparator.naturalOrder());
Expand All @@ -48,24 +53,40 @@ protected JarClassEntry(String name, String fullyQualifiedName) {
this.implementers = new ArrayList<>();
}

protected void populate(int access, String signature, String superclass, String[] interfaces) {
this.setAccess(access);
this.signature = signature;
this.superclass = superclass;
this.interfaces = Arrays.asList(interfaces);
protected void populate(Populator populator) {
this.setAccess(populator.access);
this.signature = populator.signature;
this.superclass = populator.superclass;
this.interfaces = Arrays.asList(populator.interfaces);
if (populator.nested) {
this.declaringClass = populator.declaringClass;
this.enclosingClass = populator.enclosingClass;
this.enclosingMethod = populator.enclosingMethod;
this.enclosingMethodDesc = populator.enclosingMethodDesc;
this.innerName = populator.innerName;
}
}

protected void populateParents(ClassStorage storage) {
protected void populateRelations(ClassStorage storage) {
JarClassEntry superEntry = getSuperClass(storage);
if (superEntry != null) {
superEntry.subclasses.add(fullyQualifiedName);
superEntry.subclasses.add(name);
}

for (JarClassEntry itf : getInterfaces(storage)) {
if (itf != null) {
itf.implementers.add(fullyQualifiedName);
itf.implementers.add(name);
}
}

JarClassEntry declaringEntry = getDeclaringClass(storage);
if (declaringEntry != null) {
declaringEntry.innerClasses.put(name, this);
}
JarClassEntry enclosingEntry = getEnclosingClass(storage);
if (enclosingEntry != null) {
enclosingEntry.innerClasses.put(name, this);
}
}

// unstable
Expand All @@ -74,10 +95,6 @@ public Collection<Pair<JarClassEntry, String>> getRelatedMethods(JarMethodEntry
return relatedMethods.getOrDefault(m.getKey(), Collections.EMPTY_SET);
}

public String getFullyQualifiedName() {
return fullyQualifiedName;
}

public String getSignature() {
return signature;
}
Expand All @@ -87,7 +104,7 @@ public String getSuperClassName() {
}

public JarClassEntry getSuperClass(ClassStorage storage) {
return storage.getClass(superclass, false);
return storage.getClass(superclass, null);
}

public List<String> getInterfaceNames() {
Expand Down Expand Up @@ -120,11 +137,43 @@ private List<JarClassEntry> toClassEntryList(ClassStorage storage, List<String>
}

return stringList.stream()
.map((s) -> storage.getClass(s, false))
.map((s) -> storage.getClass(s, null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

public String getDeclaringClassName() {
return declaringClass;
}

public JarClassEntry getDeclaringClass(ClassStorage storage) {
return hasDeclaringClass() ? storage.getClass(declaringClass, null) : null;
}

public String getEnclosingClassName() {
return enclosingClass;
}

public JarClassEntry getEnclosingClass(ClassStorage storage) {
return hasEnclosingClass() ? storage.getClass(enclosingClass, null) : null;
}

public String getEnclosingMethodName() {
return enclosingMethod;
}

public String getEnclosingMethodDescriptor() {
return enclosingMethodDesc;
}

public JarMethodEntry getEnclosingMethod(JarRootEntry storage) {
return hasEnclosingClass() && hasEnclosingMethod() ? getEnclosingClass(storage).getMethod(enclosingMethod + enclosingMethodDesc) : null;
}

public String getInnerName() {
return innerName;
}

public JarClassEntry getInnerClass(String name) {
return innerClasses.get(name);
}
Expand Down Expand Up @@ -153,20 +202,29 @@ public boolean isInterface() {
return Access.isInterface(getAccess());
}

public boolean hasDeclaringClass() {
return declaringClass != null;
}

public boolean hasEnclosingClass() {
return enclosingClass != null;
}

public boolean hasEnclosingMethod() {
return enclosingMethod != null;
}

public boolean isAnonymous() {
return getName().matches("[0-9]+");
return hasEnclosingClass() && innerName == null;
}

@Override
public String getKey() {
return getFullyQualifiedName();
public boolean isLocal() {
return hasEnclosingClass() && innerName != null;
}

public void remap(Remapper remapper) {
String oldName = fullyQualifiedName;
fullyQualifiedName = remapper.map(fullyQualifiedName);
String[] s = fullyQualifiedName.split("\\$");
name = s[s.length - 1];
String oldName = name;
name = remapper.map(name);

if (superclass != null) {
superclass = remapper.map(superclass);
Expand Down Expand Up @@ -209,4 +267,27 @@ public void remap(Remapper remapper) {
relatedMethods.put(methodKeyRemaps.getOrDefault(entry.getKey(), entry.getKey()), entry.getValue());
}
}

public static class Populator {
public int access;
public String name;
public String signature;
public String superclass;
public String[] interfaces;
public String declaringClass;
public String enclosingClass;
public String enclosingMethod;
public String enclosingMethodDesc;
public String innerName;

boolean nested;

public Populator(int access, String name, String signature, String superclass, String[] interfaces) {
this.access = access;
this.name = name;
this.signature = signature;
this.superclass = superclass;
this.interfaces = interfaces;
}
}
}
Loading

0 comments on commit 2d3887d

Please sign in to comment.