Skip to content

Commit 51e94fb

Browse files
committed
Add context action for clearing variable metadata on methods
Man, fuck Kotlin. // "it" variable on stack, cast to iterable then stored 218: checkcast #600 // class java/lang/Iterable 221: astore 11 223: nop // "it" variable starts here 224: iconst_0 225: istore 12 // "it" variable first used here 227: aload 11 229: astore 13 231: new #988 // class java/util/ArrayList 234: dup Bunch of junk between the "start" and actual write, which makes it really confusing when parsing from ASM's model. This is a workaround. A proper fix should target upstream JASM - https://github.com/jumanji144/Jasm/blob/c5a2c9050312497f53378f8c6a397ec3d3f02ef6/jasm-composition-jvm/src/main/java/me/darknet/assembler/printer/JvmMethodPrinter.java#L92
1 parent 732c035 commit 51e94fb

File tree

4 files changed

+98
-0
lines changed

4 files changed

+98
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package software.coley.recaf.util.visitors;
2+
3+
import jakarta.annotation.Nullable;
4+
import org.objectweb.asm.AnnotationVisitor;
5+
import org.objectweb.asm.ClassVisitor;
6+
import org.objectweb.asm.Label;
7+
import org.objectweb.asm.MethodVisitor;
8+
import org.objectweb.asm.TypePath;
9+
import software.coley.recaf.RecafConstants;
10+
import software.coley.recaf.util.AccessFlag;
11+
12+
/**
13+
* A visitor that removes method variables.
14+
* Generally useful for fixing kotlin classes since their variable tables are all sorts of wrong.
15+
*
16+
* @author Matt Coley
17+
*/
18+
public class MethodVariableRemovingVisitor extends ClassVisitor {
19+
private final MemberPredicate predicate;
20+
21+
/**
22+
* @param cv
23+
* Parent visitor.
24+
* @param predicate
25+
* Predicate to match which methods will be cleaned, or {@code null} to clean all methods.
26+
*/
27+
public MethodVariableRemovingVisitor(@Nullable ClassVisitor cv, @Nullable MemberPredicate predicate) {
28+
super(RecafConstants.getAsmVersion(), cv);
29+
30+
this.predicate = predicate;
31+
}
32+
33+
@Override
34+
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
35+
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
36+
37+
// Skip if already no code.
38+
if (AccessFlag.isAbstract(access) || AccessFlag.isNative(access))
39+
return mv;
40+
41+
// Only clean matched methods.
42+
if (predicate == null || predicate.matchMethod(access, name, descriptor, signature, exceptions))
43+
return new VarRemovingVisitor(mv);
44+
45+
return mv;
46+
}
47+
48+
/**
49+
* Method visitor that removes any local variable debug info.
50+
*/
51+
public static class VarRemovingVisitor extends MethodVisitor {
52+
public VarRemovingVisitor(@Nullable MethodVisitor mv) {
53+
super(RecafConstants.getAsmVersion(), mv);
54+
}
55+
56+
@Override
57+
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
58+
// skip
59+
}
60+
61+
@Override
62+
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) {
63+
// skip
64+
return null;
65+
}
66+
}
67+
}

recaf-ui/src/main/java/software/coley/recaf/services/cell/context/BasicMethodContextMenuProviderFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public ContextMenuProvider getMethodContextMenuProvider(@Nonnull ContextSource s
8181
JvmClassInfo declaringJvmClass = declaringClass.asJvmClass();
8282

8383
edit.item("menu.edit.copy", COPY_FILE, () -> actions.copyMember(workspace, resource, jvmBundle, declaringJvmClass, method));
84+
edit.item("menu.edit.removevars", CIRCLE_DASH, () -> actions.removeMethodVariables(workspace, resource, jvmBundle, declaringJvmClass, List.of(method)));
8485
if (!method.getName().equals("<init>")) // The conditions for optimally no-op'ing a constructor are a bit tricky, we'll just skip those for now.
8586
edit.item("menu.edit.noop", CIRCLE_DASH, () -> actions.makeMethodsNoop(workspace, resource, jvmBundle, declaringJvmClass, List.of(method)));
8687
edit.item("menu.edit.delete", TRASH_CAN, () -> actions.deleteClassMethods(workspace, resource, jvmBundle, declaringJvmClass, List.of(method)));

recaf-ui/src/main/java/software/coley/recaf/services/navigation/Actions.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
import software.coley.recaf.util.visitors.MethodAnnotationRemovingVisitor;
104104
import software.coley.recaf.util.visitors.MethodNoopingVisitor;
105105
import software.coley.recaf.util.visitors.MethodPredicate;
106+
import software.coley.recaf.util.visitors.MethodVariableRemovingVisitor;
106107
import software.coley.recaf.workspace.PathExportingManager;
107108
import software.coley.recaf.workspace.model.BasicWorkspace;
108109
import software.coley.recaf.workspace.model.Workspace;
@@ -2098,6 +2099,34 @@ public void makeMethodsNoop(@Nonnull Workspace workspace,
20982099
.build());
20992100
}
21002101

2102+
/**
2103+
* Removes variable debug info in the given methods.
2104+
*
2105+
* @param workspace
2106+
* Containing workspace.
2107+
* @param resource
2108+
* Containing resource.
2109+
* @param bundle
2110+
* Containing bundle.
2111+
* @param declaringClass
2112+
* Class declaring the methods.
2113+
* @param methods
2114+
* Methods to clean.
2115+
*/
2116+
public void removeMethodVariables(@Nonnull Workspace workspace,
2117+
@Nonnull WorkspaceResource resource,
2118+
@Nonnull JvmClassBundle bundle,
2119+
@Nonnull JvmClassInfo declaringClass,
2120+
@Nonnull Collection<MethodMember> methods) {
2121+
ClassReader reader = declaringClass.getClassReader();
2122+
ClassWriter writer = new ClassWriter(reader, 0);
2123+
MethodVariableRemovingVisitor visitor = new MethodVariableRemovingVisitor(writer, MethodPredicate.of(methods));
2124+
reader.accept(visitor, declaringClass.getClassReaderFlags());
2125+
bundle.put(declaringClass.toJvmClassBuilder()
2126+
.adaptFrom(writer.toByteArray())
2127+
.build());
2128+
}
2129+
21012130
/**
21022131
* @param bundle
21032132
* Containing bundle.

recaf-ui/src/main/resources/translations/en_US.lang

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ menu.edit.remove=Remove
4848
menu.edit.copy=Copy
4949
menu.edit.delete=Delete
5050
menu.edit.noop=Make no-op
51+
menu.edit.removevars=Prune variable info
5152
menu.edit.changeversion=Change class versions
5253
menu.edit.changeversion.up=Upgrade
5354
menu.edit.changeversion.down=Downgrade

0 commit comments

Comments
 (0)