Skip to content

Commit

Permalink
Fixed search reference not working
Browse files Browse the repository at this point in the history
  • Loading branch information
tth05 committed Oct 25, 2021
1 parent a9b86b1 commit 6cf2d96
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 389 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,18 @@ public void execute(@Nonnull MinecraftServer server, @Nonnull ICommandSender sen
throw new CommandException("commands.total_debug.searchreference.usage");
}

if (!args[1].matches("^([\\w/$]+\\.)?\\w[\\w/$();]+$")) {
throw new CommandException("commands.total_debug.searchreference.usage_examples");
}

long t = System.nanoTime() / 1_000_000;

boolean searchMethod = args[0].equalsIgnoreCase("method");
CompletableFuture<Pair<Collection<String>, Integer>> future = BytecodeReferenceSearcher.findReferences(args[1], searchMethod);

int dotIndex = args[1].indexOf('.');
String owner = dotIndex == -1 ? null : args[1].substring(0, dotIndex);
String toMatch = dotIndex == -1 ? args[1] : args[1].substring(dotIndex + 1);
CompletableFuture<Pair<Collection<String>, Integer>> future = BytecodeReferenceSearcher.findReferences(owner, toMatch, searchMethod);
if (future == null) {
throw new CommandException("commands.total_debug.searchreference.already_running");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
import com.github.minecraft_ta.totaldebug.TotalDebug;
import net.minecraft.launchwrapper.LaunchClassLoader;
import org.apache.commons.lang3.tuple.Pair;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class BytecodeReferenceSearcher {
Expand All @@ -22,7 +27,7 @@ public class BytecodeReferenceSearcher {
* @param searchMethod true if you want to search for methods; false otherwise
*/
@Nullable
public static CompletableFuture<Pair<Collection<String>, Integer>> findReferences(String signature, boolean searchMethod) {
public static CompletableFuture<Pair<Collection<String>, Integer>> findReferences(String owner, String signature, boolean searchMethod) {
if (RUNNING)
return null;

Expand Down Expand Up @@ -55,14 +60,13 @@ public static CompletableFuture<Pair<Collection<String>, Integer>> findReference
int finalEndIndex = endIndex;
tasks.add(() -> {
List<String> subResults = new ArrayList<>();
InternalRemappingContext context = new InternalRemappingContext(subResults, signature, searchMethod);
ReferenceVisitor context = new ReferenceVisitor(subResults, owner, signature, searchMethod);

for (int j = startIndex; j <= finalEndIndex; j++) {
Class<?> clazz = allClasses.get(j);
context.currentClass = clazz;

//remap and search
RemappingUtil.getRemappedClass(clazz, context);
byte[] bytes = ClassUtil.getBytecodeFromLaunchClassLoader(allClasses.get(j).getName());
if (bytes == null)
continue;
new ClassReader(bytes).accept(context, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
}

return subResults;
Expand Down Expand Up @@ -116,43 +120,161 @@ private static List<Class<?>> getFilteredClassesList() {
}
}

private static final class InternalRemappingContext extends RemappingUtil.RemappingContext {
private static final class ReferenceVisitor extends ClassVisitor {

private final List<String> results;
private final String owner;
private final String signatureToMatch;
private final boolean method;

private Class<?> currentClass;
private String currentClassName;

public InternalRemappingContext(List<String> results, String signatureToMatch, boolean method) {
public ReferenceVisitor(List<String> results, String owner, String signatureToMatch, boolean method) {
super(Opcodes.ASM5, null);
this.results = results;
this.owner = owner;
this.signatureToMatch = signatureToMatch;
write = false;
mapMethodInsn = method;
mapFieldInsn = !method;
mapTypeAndLdcInsn = false;
mapFields = false;
mapLocals = false;
this.method = method;
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.currentClassName = name;
super.visit(version, access, name, signature, superName, interfaces);
}

@Override
public void onMethodInsnMapping(@Nonnull String containedMethodName, @Nonnull String newMethodSignature) {
if (newMethodSignature.endsWith(signatureToMatch)) {
Class<?> foundClass = RemappingUtil.tryFindClassWithMappings(currentClass.getName());
if (foundClass == null)
return;
public MethodVisitor visitMethod(int access, String enclosingMethodName, String desc, String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM5, null) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (!method || !(name + desc).endsWith(ReferenceVisitor.this.signatureToMatch))
return;
if (ReferenceVisitor.this.owner == null) {
ReferenceVisitor.this.results.add(ReferenceVisitor.this.currentClassName + "#" + enclosingMethodName);
return;
}

boolean valid;
if (owner.endsWith(ReferenceVisitor.this.owner)) {
valid = true;
} else { //Try finding actual owner of method
String actualOwner = findOwnerOf(owner, name, desc, true);
valid = actualOwner != null && actualOwner.endsWith(ReferenceVisitor.this.owner);
}

if (valid)
ReferenceVisitor.this.results.add(ReferenceVisitor.this.currentClassName + "#" + enclosingMethodName);
}

@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (method || !name.endsWith(ReferenceVisitor.this.signatureToMatch))
return;

if (ReferenceVisitor.this.owner == null) {
ReferenceVisitor.this.results.add(ReferenceVisitor.this.currentClassName + "#" + enclosingMethodName);
return;
}

boolean valid;
if (owner.endsWith(ReferenceVisitor.this.owner)) {
valid = true;
} else { //Try finding actual owner of field
String actualOwner = findOwnerOf(owner, name, desc, false);
valid = actualOwner != null && actualOwner.endsWith(ReferenceVisitor.this.owner);
}

if (valid)
ReferenceVisitor.this.results.add(ReferenceVisitor.this.currentClassName + "#" + enclosingMethodName);
}
};
}

private String findOwnerOf(String currentOwner, String name, String desc, boolean method) {
try {
List<Class<?>> interfaces = new ArrayList<>();
Class<?> clazz = Class.forName(currentOwner.replace('/', '.'));
if (method)
interfaces.addAll(Arrays.asList(clazz.getInterfaces()));

Function<Class<?>, String> checkClass = (c) -> {
if (method) {
for (Method declaredMethod : c.getDeclaredMethods()) {
if (declaredMethod.getName().endsWith(name) && getMethodDesc(declaredMethod).equals(desc)) {
return c.getName().replace('.', '/');
}
}
} else {
for (Field field : c.getFields()) {
if (field.getName().endsWith(name)) {
return c.getName().replace('.', '/');
}
}
}

return null;
};

while ((clazz = clazz.getSuperclass()) != null) {
String result = checkClass.apply(clazz);
if (result != null)
return result;
if (method)
interfaces.addAll(Arrays.asList(clazz.getInterfaces()));
}

for (Class<?> i : interfaces) {
String result = checkClass.apply(i);
if (result != null)
return result;
}

results.add(foundClass.getName().replace('.', '/') + "#" + containedMethodName);
return null;
} catch (Exception e) {
return null;
}
}

@Override
public void onFieldInsnMapping(@Nonnull String containedMethodName, @Nonnull String newFieldSignature) {
if (newFieldSignature.endsWith(signatureToMatch)) {
Class<?> foundClass = RemappingUtil.tryFindClassWithMappings(currentClass.getName());
if (foundClass == null)
return;
private String getMethodDesc(Method declaredMethod) {
StringBuilder descBuilder = new StringBuilder("(");
for (Class<?> parameterType : declaredMethod.getParameterTypes()) {
appendDescType(descBuilder, parameterType.getName());
}

descBuilder.append(")");
appendDescType(descBuilder, declaredMethod.getReturnType().getName());
return descBuilder.toString().replace('.', '/');
}

results.add(foundClass.getName().replace('.', '/') + "#" + containedMethodName);
private void appendDescType(StringBuilder builder, String type) {
switch (type) {
case "byte":
builder.append("B");
break;
case "char":
builder.append("C");
case "double":
builder.append("D");
break;
case "float":
builder.append("F");
break;
case "int":
builder.append("I");
break;
case "long":
builder.append("J");
break;
case "short":
builder.append("S");
break;
case "void":
builder.append("V");
break;
default:
builder.append("L").append(type).append(";");
break;
}
}
}
Expand Down
Loading

0 comments on commit 6cf2d96

Please sign in to comment.