Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/matcher/Matcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public void initFromMatches(List<Path> inputDirs,
List<Path> classPathA = resolvePaths(inputDirs, cpFilesA);
List<Path> classPathB = resolvePaths(inputDirs, cpFilesB);

ProjectConfig config = new ProjectConfig.Builder(pathsA, pathsB)
ProjectConfig config = ProjectConfig.builder(pathsA, pathsB)
.classPathA(new ArrayList<>(classPathA))
.classPathB(new ArrayList<>(classPathB))
.sharedClassPath(new ArrayList<>(sharedClassPath))
Expand Down
20 changes: 12 additions & 8 deletions src/main/java/matcher/config/ProjectConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
import java.util.regex.PatternSyntaxException;

public class ProjectConfig {
public static Builder builder(List<Path> pathsA, List<Path> pathsB) {
return new Builder(pathsA, pathsB);
}

public static class Builder {
public Builder(List<Path> pathsA, List<Path> pathsB) {
private Builder(List<Path> pathsA, List<Path> pathsB) {
this.pathsA = pathsA;
this.pathsB = pathsB;
}
Expand Down Expand Up @@ -97,17 +101,17 @@ public ProjectConfig build() {

protected final List<Path> pathsA;
protected final List<Path> pathsB;
protected List<Path> classPathA;
protected List<Path> classPathB;
protected List<Path> sharedClassPath;
protected List<Path> classPathA = Collections.emptyList();
protected List<Path> classPathB = Collections.emptyList();
protected List<Path> sharedClassPath = Collections.emptyList();
protected boolean inputsBeforeClassPath;
protected Path mappingsPathA;
protected Path mappingsPathB;
protected boolean saveUnmappedMatches = true;
protected String nonObfuscatedClassPatternA;
protected String nonObfuscatedClassPatternB;
protected String nonObfuscatedMemberPatternA;
protected String nonObfuscatedMemberPatternB;
protected String nonObfuscatedClassPatternA = "";
protected String nonObfuscatedClassPatternB = "";
protected String nonObfuscatedMemberPatternA = "";
protected String nonObfuscatedMemberPatternB = "";
}

private ProjectConfig(List<Path> pathsA, List<Path> pathsB, List<Path> classPathA, List<Path> classPathB,
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/matcher/gui/Gui.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ private void handleStartupArgs(List<String> args) {

if (!validProjectConfigArgPresent) return;

ProjectConfig config = new ProjectConfig.Builder(inputsA, inputsB)
ProjectConfig config = ProjectConfig.builder(inputsA, inputsB)
.classPathA(new ArrayList<>(classPathA))
.classPathB(new ArrayList<>(classPathB))
.sharedClassPath(new ArrayList<>(sharedClassPath))
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/matcher/gui/menu/NewProjectPane.java
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ private Node createMiscPane() {
}

public ProjectConfig createConfig() {
return new ProjectConfig.Builder(new ArrayList<>(pathsA), new ArrayList<>(pathsB))
return ProjectConfig.builder(new ArrayList<>(pathsA), new ArrayList<>(pathsB))
.classPathA(new ArrayList<>(classPathA))
.classPathB(new ArrayList<>(classPathB))
.sharedClassPath(new ArrayList<>(sharedClassPath))
Expand Down
204 changes: 204 additions & 0 deletions src/main/java/matcher/mapping/Matcher2MioHierarchyProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package matcher.mapping;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;

import net.fabricmc.mappingio.tree.HierarchyInfoProvider;
import net.fabricmc.mappingio.tree.MappingTreeView;
import net.fabricmc.mappingio.tree.MappingTreeView.MethodMappingView;

import matcher.Util;
import matcher.mapping.Matcher2MioHierarchyProvider.HierarchyData;
import matcher.type.ClassEnv;
import matcher.type.ClassInstance;
import matcher.type.FieldInstance;
import matcher.type.LocalClassEnv;
import matcher.type.MethodInstance;
import matcher.type.MethodVarInstance;

public class Matcher2MioHierarchyProvider implements HierarchyInfoProvider<HierarchyData> {
public Matcher2MioHierarchyProvider(LocalClassEnv env, String namespace) {
this.env = env;
this.namespace = namespace;
}

@Override
public String getNamespace() {
return namespace;
}

@Override
public String resolveField(String owner, String name, String desc) {
ClassInstance cls = env.getClsByName(owner);
if (cls == null) return null;

FieldInstance field = cls.resolveField(name, desc);

return field != null ? field.getOwner().getName() : null;
}

@Override
public String resolveMethod(String owner, String name, String desc) {
ClassInstance cls = env.getClsByName(owner);
if (cls == null) return null;

MethodInstance method = cls.resolveMethod(name, desc, cls.isInterface());

return method != null ? method.getOwner().getName() : null;
}

@Override
public HierarchyData getMethodHierarchy(String owner, String name, String desc) {
ClassInstance cls = env.getClsByName(owner);
if (cls == null) return null;

MethodInstance method = cls.resolveMethod(name, desc, cls.isInterface());
if (method == null) return null;

assert !method.isStatic() || method.getAllHierarchyMembers().size() == 1;

return getMethodHierarchy0(method);
}

private HierarchyData getMethodHierarchy0(MethodInstance method) {
Set<MethodInstance> immediateHierarchy = method.getAllHierarchyMembers();
Queue<Set<MethodInstance>> toCheck = new ArrayDeque<>(immediateHierarchy.size());
Set<Set<MethodInstance>> checked = new HashSet<>();
toCheck.add(immediateHierarchy);

do {
Set<MethodInstance> currentHierarchy;

while ((currentHierarchy = toCheck.poll()) != null) {
if (checked.contains(currentHierarchy)) {
continue;
}

for (MethodInstance m : currentHierarchy) {
if (m.isSynthetic()) {
Set<MethodInstance> targets = m.getRefsOut();

for (MethodInstance target : targets) {
if (isPotentialBridge(m, target)) {
toCheck.add(target.getAllHierarchyMembers());
}
}
}

Set<MethodInstance> sources = findSyntheticSources(m);

for (MethodInstance source : sources) {
if (isPotentialBridge(source, m)) {
toCheck.add(source.getAllHierarchyMembers());
}
}
}

checked.add(currentHierarchy);
}
} while (!toCheck.isEmpty());

Set<MethodInstance> fullHierarchy = Util.newIdentityHashSet();

for (Set<MethodInstance> hierarchy : checked) {
fullHierarchy.addAll(hierarchy);
}

return new HierarchyData(fullHierarchy);
}

private Set<MethodInstance> findSyntheticSources(MethodInstance method) {
Set<MethodInstance> sources = Collections.emptySet();

for (MethodInstance refIn : method.getRefsIn()) {
if (refIn.isSynthetic()) {
if (sources.isEmpty()) {
sources = new HashSet<>();
}

sources.add(refIn);
}
}

return sources;
}

private boolean isPotentialBridge(MethodInstance bridgeMethod, MethodInstance bridgedMethod) {
if (!bridgeMethod.isSynthetic()) return false;

if (bridgeMethod.isPrivate() || bridgeMethod.isFinal() || bridgeMethod.isStatic()) {
return false;
}

// Bridge method's target must be in the same class or in a parent class
if (!bridgedMethod.getOwner().isAssignableFrom(bridgeMethod.getOwner())) {
return false;
}

MethodVarInstance[] bridgeParams = bridgeMethod.getArgs();
MethodVarInstance[] bridgedParams = bridgedMethod.getArgs();

if (bridgeParams.length != bridgedParams.length) {
return false;
}

for (int i = 0; i < bridgeParams.length; i++) {
if (!areTypesBridgeCompatible(bridgeParams[i].getType(), bridgedParams[i].getType())) {
return false;
}
}

return areTypesBridgeCompatible(bridgeMethod.getRetType(), bridgedMethod.getRetType());
}

private boolean areTypesBridgeCompatible(ClassInstance bridgeType, ClassInstance bridgedType) {
if (bridgeType.equals(bridgedType)) {
return true;
}

boolean bridgedExtendsBridge = bridgeType.isAssignableFrom(bridgedType);

// If not equal, types in bridge method descriptor should always be less specific than in the bridged method
assert bridgedExtendsBridge || !bridgedType.isAssignableFrom(bridgeType);

return bridgedExtendsBridge;
}

@Override
public int getHierarchySize(HierarchyData hierarchy) {
return hierarchy != null ? hierarchy.methods.size() : 0;
}

@Override
public Collection<? extends MethodMappingView> getHierarchyMethods(HierarchyData hierarchy, MappingTreeView tree) {
if (hierarchy == null) return Collections.emptyList();

List<MethodMappingView> ret = new ArrayList<>(hierarchy.methods.size());
int ns = tree.getNamespaceId(namespace);
assert ns != MappingTreeView.NULL_NAMESPACE_ID;

for (MethodInstance method : hierarchy.methods) {
MethodMappingView m = tree.getMethod(method.getOwner().getName(), method.getName(), method.getDesc(), ns);
if (m != null) ret.add(m);
}

return ret;
}

public static final class HierarchyData {
HierarchyData(Collection<MethodInstance> methods) {
this.methods = methods;
}

final Collection<MethodInstance> methods;
}

private final ClassEnv env;
private final String namespace;
}
12 changes: 6 additions & 6 deletions src/main/java/matcher/type/ClassInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -1001,14 +1001,14 @@ public boolean isAssignableFrom(ClassInstance c) {

// check if c or a superclass of c implements this with multiple indirections
if (toCheck != null) {
while ((sc = toCheck.poll()) != null) {
for (ClassInstance iface : sc.getInterfaces()) {
assert iface != this; // already checked
ClassInstance iface;

if (implementers.contains(iface)) return true;
while ((iface = toCheck.poll()) != null) {
assert iface != this; // already checked

toCheck.addAll(iface.interfaces);
}
if (implementers.contains(iface)) return true;

toCheck.addAll(iface.interfaces);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/matcher/type/MemberInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public String getDisplayName(NameType type, boolean full) {
public abstract boolean isReal();

@Override
public Matchable<?> getOwner() {
public ClassInstance getOwner() {
return cls;
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/matcher/type/MethodVarInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ private boolean hasValidOrigName() {
}

@Override
public Matchable<?> getOwner() {
public MethodInstance getOwner() {
return method;
}

Expand Down