Rewrite constant parameter. A bit faster now. Can't tell if its right. Seems to compile.
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -31,7 +31,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-all</artifactId>
|
||||
<artifactId>asm-debug-all</artifactId>
|
||||
<version>5.0.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -10,25 +10,73 @@ import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.InvokeInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
|
||||
import net.runelite.asm.execution.Execution;
|
||||
import net.runelite.asm.execution.Frame;
|
||||
import net.runelite.asm.execution.InstructionContext;
|
||||
import net.runelite.asm.pool.NameAndType;
|
||||
import net.runelite.asm.pool.PoolEntry;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import net.runelite.asm.execution.StackContext;
|
||||
import net.runelite.asm.signature.util.VirtualMethods;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
public class UnusedParameters implements Deobfuscator
|
||||
{
|
||||
private List<Integer> findUnusedParameters(Method method)
|
||||
{
|
||||
private Map<List<Method>, Collection<Integer>> unused = new HashMap<>();
|
||||
|
||||
private void buildUnused(ClassGroup group)
|
||||
{
|
||||
unused.clear();
|
||||
|
||||
for (ClassFile cf : group.getClasses())
|
||||
for (Method m : cf.getMethods().getMethods())
|
||||
{
|
||||
if (!Deob.isObfuscated(m.getName()))
|
||||
continue;
|
||||
|
||||
List<Method> ms = VirtualMethods.getVirutalMethods(m);
|
||||
Collection<Integer> u = this.findUnusedParameters(ms);
|
||||
if (!u.isEmpty())
|
||||
unused.put(ms, u);
|
||||
}
|
||||
}
|
||||
|
||||
private int processUnused(Execution execution, ClassGroup group)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (List<Method> m : unused.keySet())
|
||||
{
|
||||
Collection<Integer> u = unused.get(m);
|
||||
|
||||
int offset = m.size() == 1 && m.get(0).isStatic() ? 0 : 1;
|
||||
|
||||
for (int unusedParameter : u)
|
||||
{
|
||||
Signature signature = m.get(0).getDescriptor();
|
||||
//int[] lvtIndexes = getLvtIndexes(signature, offset);//XX reverse this
|
||||
int lvtIndex = this.getLvtIndex(signature, offset, unusedParameter);
|
||||
/* removing the parameter can't cause collisions on other (overloaded) methods because prior to this we rename
|
||||
* all classes/fields/methods to have unique names.
|
||||
*/
|
||||
System.out.println("Removing parameter " + unusedParameter + " at index " + lvtIndex + " from " + m);
|
||||
removeParameter(group, m, signature, execution, unusedParameter, lvtIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
++count;
|
||||
|
||||
//break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public List<Integer> findUnusedParameters(Method method)
|
||||
{
|
||||
int offset = method.isStatic() ? 0 : 1;
|
||||
Signature signature = method.getNameAndType().getDescriptor();
|
||||
@@ -46,19 +94,18 @@ public class UnusedParameters implements Deobfuscator
|
||||
return unusedParams;
|
||||
}
|
||||
|
||||
private int[] getLvtIndexes(Signature signature, int offset)
|
||||
@SuppressWarnings("empty-statement")
|
||||
private int getLvtIndex(Signature signature, int offset, int parameter)
|
||||
{
|
||||
int[] i = new int[signature.size()];
|
||||
for (int variableIndex = 0, lvtIndex = offset;
|
||||
variableIndex < signature.size();
|
||||
lvtIndex += signature.getTypeOfArg(variableIndex++).getSlots())
|
||||
{
|
||||
i[variableIndex] = lvtIndex;
|
||||
}
|
||||
return i;
|
||||
// get lvt index for parameter
|
||||
int lvtIndex = offset;
|
||||
for (int variableIndex = 0;
|
||||
variableIndex < parameter;
|
||||
lvtIndex += signature.getTypeOfArg(variableIndex++).getSlots());
|
||||
return lvtIndex;
|
||||
}
|
||||
|
||||
private Collection<Integer> findUnusedParameters(Collection<Method> methods)
|
||||
public Collection<Integer> findUnusedParameters(Collection<Method> methods)
|
||||
{
|
||||
Collection<Integer> list = null;
|
||||
|
||||
@@ -72,82 +119,56 @@ public class UnusedParameters implements Deobfuscator
|
||||
list = CollectionUtils.intersection(list, p);
|
||||
}
|
||||
|
||||
return list;
|
||||
List<Integer> l = new ArrayList<>(list);
|
||||
Collections.sort(l);
|
||||
Collections.reverse(l);
|
||||
return l;
|
||||
}
|
||||
|
||||
public void removeParameter(List<Method> methods, Signature signature, Execution execution, int paramIndex, int lvtIndex)
|
||||
public void removeParameter(ClassGroup group, List<Method> methods, Signature signature, Execution execution, int paramIndex, int lvtIndex)
|
||||
{
|
||||
Set<Instruction> done = new HashSet<>();
|
||||
int slots = signature.getTypeOfArg(paramIndex).getSlots();
|
||||
|
||||
assert signature.getTypeOfArg(paramIndex).getSlots() == 1;
|
||||
|
||||
for (Frame f : execution.processedFrames)
|
||||
for (InstructionContext ins : f.getInstructions())
|
||||
if (!ins.getInvokes().isEmpty() && methods.containsAll(ins.getInvokes()))
|
||||
for (ClassFile cf : group.getClasses())
|
||||
for (Method m : cf.getMethods().getMethods())
|
||||
{
|
||||
Code c = m.getCode();
|
||||
|
||||
if (c == null)
|
||||
continue;
|
||||
|
||||
for (Instruction i : new ArrayList<>(c.getInstructions().getInstructions()))
|
||||
{
|
||||
int pops = signature.size() - paramIndex - 1; // index from top of stack of parameter. 0 is the last parameter
|
||||
|
||||
StackContext sctx = ins.getPops().get(pops);
|
||||
if (sctx.getPushed().getInstruction().getInstructions() == null)
|
||||
if (!(i instanceof InvokeInstruction))
|
||||
continue;
|
||||
|
||||
ins.removeStack(pops); // remove parameter from stack
|
||||
InvokeInstruction ii = (InvokeInstruction) i;
|
||||
|
||||
if (done.contains(ins.getInstruction()))
|
||||
if (!ii.getMethods().containsAll(methods))
|
||||
continue;
|
||||
|
||||
InvokeInstruction iins = (InvokeInstruction) ins.getInstruction();
|
||||
iins.removeParameter(paramIndex); // remove parameter from instruction
|
||||
ii.removeParameter(paramIndex); // remove parameter from instruction
|
||||
|
||||
done.add(ins.getInstruction());
|
||||
}
|
||||
|
||||
for (Method method : methods)
|
||||
// this double checks that all calls to this have been located
|
||||
for (ClassFile cf : method.getMethods().getClassFile().getGroup().getClasses())
|
||||
for (Method m : cf.getMethods().getMethods())
|
||||
{
|
||||
Code c = m.getCode();
|
||||
if (c == null)
|
||||
continue;
|
||||
|
||||
for (Instruction i : c.getInstructions().getInstructions())
|
||||
Collection<InstructionContext> ics = execution.getInstructonContexts(i);
|
||||
if (ics != null)
|
||||
{
|
||||
if (i instanceof InvokeInstruction)
|
||||
{
|
||||
InvokeInstruction ii = (InvokeInstruction) i;
|
||||
PoolEntry pool = ii.getMethod();
|
||||
|
||||
if (pool instanceof net.runelite.asm.pool.Method)
|
||||
{
|
||||
net.runelite.asm.pool.Method pm = (net.runelite.asm.pool.Method) pool;
|
||||
|
||||
if (pm.getClassEntry().getName().equals(method.getMethods().getClassFile().getName()) && pm.getNameAndType().equals(method.getNameAndType()) && !done.contains(i))
|
||||
{
|
||||
// for some reason this wasn't removed above?
|
||||
System.err.println("Method " + m.getName() + " in " + cf.getName() + " calls " + pm.getNameAndType().getName() + " in " + pm.getClassEntry().getName() + " at " + i.getPc() + ", but this instruction was not found during execution");
|
||||
//assert false;
|
||||
}
|
||||
}
|
||||
else if (pool instanceof net.runelite.asm.pool.InterfaceMethod)
|
||||
{
|
||||
net.runelite.asm.pool.InterfaceMethod pm = (net.runelite.asm.pool.InterfaceMethod) pool;
|
||||
|
||||
if (pm.getClassEntry().getName().equals(method.getMethods().getClassFile().getName()) && pm.getNameAndType().equals(method.getNameAndType()) && !done.contains(i))
|
||||
{
|
||||
// for some reason this wasn't removed above?
|
||||
System.err.println("Method " + m.getName() + " in " + cf.getName() + " calls " + pm.getNameAndType().getName() + " in " + pm.getClassEntry().getName() + " at " + i.getPc() + ", but this instruction was not found during execution");
|
||||
//assert false;
|
||||
}
|
||||
}
|
||||
}
|
||||
InstructionContext ins = ics.toArray(new InstructionContext[0])[0];
|
||||
|
||||
int pops = signature.size() - paramIndex - 1; // index from top of stack of parameter. 0 is the last parameter
|
||||
|
||||
StackContext sctx = ins.getPops().get(pops);
|
||||
if (sctx.getPushed().getInstruction().getInstructions() == null)
|
||||
continue;
|
||||
|
||||
ins.removeStack(pops); // remove parameter from stack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Method method : methods)
|
||||
if (method.getCode() != null)
|
||||
// adjust lvt indexes to get rid of idx in the method
|
||||
for (Instruction ins : new ArrayList<>(method.getCode().getInstructions().getInstructions()))
|
||||
for (Instruction ins : method.getCode().getInstructions().getInstructions())
|
||||
{
|
||||
if (ins instanceof LVTInstruction)
|
||||
{
|
||||
@@ -160,8 +181,9 @@ public class UnusedParameters implements Deobfuscator
|
||||
if (i > lvtIndex)
|
||||
{
|
||||
assert i > 0;
|
||||
assert i >= lvtIndex + slots;
|
||||
|
||||
Instruction newIns = lins.setVariableIndex(--i);
|
||||
Instruction newIns = lins.setVariableIndex(i - slots);
|
||||
assert ins == newIns;
|
||||
// this doesn't work because we'd have to reexecute or the above Frames would be messing with these instructions
|
||||
//if (newIns != ins)
|
||||
@@ -173,57 +195,6 @@ public class UnusedParameters implements Deobfuscator
|
||||
for (Method method : methods)
|
||||
method.arguments.remove(paramIndex);
|
||||
}
|
||||
|
||||
private int checkParametersOnce(Execution execution, ClassGroup group)
|
||||
{
|
||||
// removing parameters shifts the others around which is annoying.
|
||||
// if more than one is unused, we'll just remove the one
|
||||
// and do the others on another pass
|
||||
|
||||
Set<Method> done = new HashSet<>();
|
||||
int count = 0;
|
||||
|
||||
for (ClassFile cf : group.getClasses())
|
||||
{
|
||||
for (Method m : cf.getMethods().getMethods())
|
||||
{
|
||||
if (done.contains(m) || !Deob.isObfuscated(m.getName()))
|
||||
continue;
|
||||
|
||||
int offset = m.isStatic() ? 0 : 1;
|
||||
Signature signature = m.getNameAndType().getDescriptor();
|
||||
|
||||
// for a parameter to be unused it must be unused on all methods that override it
|
||||
|
||||
List<Method> methods = VirtualMethods.getVirutalMethods(m);
|
||||
|
||||
Collection<Integer> unusedParameters = findUnusedParameters(methods);
|
||||
|
||||
if (unusedParameters.isEmpty())
|
||||
continue;
|
||||
|
||||
int unusedParameter = (int) unusedParameters.toArray()[0];
|
||||
int[] lvtIndexes = getLvtIndexes(signature, offset);
|
||||
|
||||
for (Method m2 : methods)
|
||||
{
|
||||
assert !done.contains(m2);
|
||||
done.add(m2);
|
||||
}
|
||||
|
||||
/* removing the parameter can't cause collisions on other (overloaded) methods because prior to this we rename
|
||||
* all classes/fields/methods to have unique names.
|
||||
*/
|
||||
System.out.println("Removing parameter " + unusedParameter + " from " + methods.get(0).getName());
|
||||
removeParameter(methods, signature, execution, unusedParameter, lvtIndexes[unusedParameter]);
|
||||
|
||||
++count;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int count;
|
||||
|
||||
@@ -231,6 +202,7 @@ public class UnusedParameters implements Deobfuscator
|
||||
public void run(ClassGroup group)
|
||||
{
|
||||
int i;
|
||||
int pnum = 1;
|
||||
do
|
||||
{
|
||||
group.buildClassGraph();
|
||||
@@ -239,7 +211,9 @@ public class UnusedParameters implements Deobfuscator
|
||||
execution.populateInitialMethods();
|
||||
execution.run();
|
||||
|
||||
i = checkParametersOnce(execution, group);
|
||||
this.buildUnused(group);
|
||||
i = this.processUnused(execution, group);
|
||||
System.out.println("PASS " + pnum++ + " " + i);
|
||||
|
||||
count += i;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ package net.runelite.deob.deobfuscators;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.signature.util.VirtualMethods;
|
||||
import net.runelite.deob.util.JarUtil;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
@@ -34,8 +37,35 @@ public class UnusedParametersTest
|
||||
|
||||
@Test
|
||||
public void testRun()
|
||||
{
|
||||
{
|
||||
RenameUnique r = new RenameUnique();
|
||||
r.run(group);
|
||||
r = null;
|
||||
System.gc();
|
||||
|
||||
RuntimeExceptions re = new RuntimeExceptions();
|
||||
re.run(group);
|
||||
re = null;
|
||||
System.gc();
|
||||
|
||||
UnreachedCode uc = new UnreachedCode();
|
||||
uc.run(group);
|
||||
uc = null;
|
||||
System.gc();
|
||||
|
||||
UnusedMethods um = new UnusedMethods();
|
||||
um.run(group);
|
||||
um = null;
|
||||
System.gc();
|
||||
|
||||
IllegalStateExceptions ise = new IllegalStateExceptions();
|
||||
ise.run(group);
|
||||
ise = null;
|
||||
System.gc();
|
||||
|
||||
UnusedParameters cp = new UnusedParameters();
|
||||
cp.run(group);
|
||||
cp = null;
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user