Remove jump graph, isn't used except for some integrity checks, but it makes stuff overly complex.

This commit is contained in:
Adam
2016-03-24 17:18:08 -04:00
parent d4a74501b7
commit cbdf406434
17 changed files with 38 additions and 955 deletions

View File

@@ -41,8 +41,6 @@ public class Code extends Attribute
this.attributes = new Attributes(this);
this.attributes.load(is);
instructions.buildJumpGraph();
}
@Override

View File

@@ -9,6 +9,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
public abstract class Instruction implements Cloneable
{
@@ -18,9 +19,6 @@ public abstract class Instruction implements Cloneable
private int pc; // offset into method this instructions resides at
protected int length = 1; // length of this instruction
public List<Instruction> jump = new ArrayList<>(), // instructions which this instruction jumps to
from = new ArrayList<>(); // instructions which jump to this instruction
public Instruction(Instructions instructions, InstructionType type, int pc)
{
this.instructions = instructions;
@@ -48,9 +46,6 @@ public abstract class Instruction implements Cloneable
throw new RuntimeException();
}
i.from = new ArrayList<>();
i.jump = new ArrayList<>();
return i;
}
@@ -60,10 +55,6 @@ public abstract class Instruction implements Cloneable
protected void remove()
{
for (Instruction i : jump)
i.from.remove(this);
jump.clear();
Exceptions exceptions = instructions.getCode().getExceptions();
for (Exception e : exceptions.getExceptions())
{
@@ -71,8 +62,12 @@ public abstract class Instruction implements Cloneable
assert this != e.getEnd();
assert this != e.getHandler();
}
assert from.isEmpty(); // because this is empty no jumping instructions point here
// XXX unreached code deob relies on being able to remove instructions that other ins jump to,
// if those other ins are also unreached.
//for (Instruction i : instructions.getInstructions())
// if (i instanceof JumpingInstruction)
// assert ((JumpingInstruction) i).getJumps().contains(this) == false;
}
public void replace(Instruction other)
@@ -83,35 +78,12 @@ public abstract class Instruction implements Cloneable
assert ins.contains(this);
assert !ins.contains(other);
// XXX instructions which hold references to instructions !
// is this really the right place for this?
for (Instruction i : ins)
{
i.replace(this, other);
}
// update instructions which jump here to jump to the new instruction
for (Instruction i : from)
{
assert i.jump.contains(this);
assert !i.jump.contains(other);
i.jump.remove(this);
i.jump.add(other);
}
from.clear();
// move jumps over
for (Instruction i : jump)
{
assert i.from.contains(this);
assert !i.from.contains(other);
i.from.remove(this);
i.from.add(other);
}
other.jump = new ArrayList<>(this.jump);
jump.clear();
Exceptions exceptions = instructions.getCode().getExceptions();
for (Exception e : exceptions.getExceptions())
{
@@ -141,17 +113,6 @@ public abstract class Instruction implements Cloneable
i.replace(this, next);
}
for (Instruction i : from)
{
assert i.jump.contains(this);
i.jump.remove(this);
i.jump.add(next);
next.from.add(i);
}
from.clear();
for (Exception e : instructions.getCode().getExceptions().getExceptions())
e.replace(this, next);
@@ -220,19 +181,6 @@ public abstract class Instruction implements Cloneable
return type.getName() + " at pc " + frame.getPc() + " in " + frame.getMethod().getName() + " " + frame.getMethod().getDescriptor() + " class " + frame.getMethod().getCode().getAttributes().getClassFile().getName();
}
protected void addJump(Instruction to)
{
assert to != null;
assert to != this;
assert this.jump.contains(to) == to.from.contains(this);
if (this.jump.contains(to))
return; // switch statements can jump to the same place multiple times
this.jump.add(to);
to.from.add(this);
}
public abstract void execute(Frame e);
/* does this terminate a block? */

View File

@@ -85,8 +85,6 @@ public class Instructions
this.regeneratePool();
// translate instructions to specific
this.buildJumpGraph();
for (Instruction i : new ArrayList<>(instructions))
{
Instruction specific = i.makeSpecific();
@@ -121,26 +119,6 @@ public class Instructions
out.write(ba);
}
public void clearJumpGraph()
{
for (Instruction i : instructions)
{
i.jump.clear();
i.from.clear();
}
}
public void buildJumpGraph()
{
clearJumpGraph();
assert new HashSet<>(instructions).size() == instructions.size();
for (Instruction i : instructions)
if (i instanceof JumpingInstruction)
((JumpingInstruction) i).buildJumpGraph();
}
public Code getCode()
{
return code;
@@ -180,21 +158,9 @@ public class Instructions
instructions.remove(oldi);
oldi.setInstructions(null);
instructions.add(i, newi);
for (Instruction ins : oldi.from)
{
assert ins.getInstructions() == this;
assert this.getInstructions().contains(ins);
assert ins.jump.contains(oldi);
ins.jump.remove(oldi);
ins.jump.add(newi);
newi.from.add(ins);
for (Instruction ins : instructions)
ins.replace(oldi, newi);
}
oldi.from.clear();
for (net.runelite.asm.attributes.code.Exception e : code.getExceptions().getExceptions())
e.replace(oldi, newi);

View File

@@ -5,7 +5,5 @@ import java.util.List;
public interface JumpingInstruction
{
public void buildJumpGraph();
List<Instruction> getJumps();
}

View File

@@ -64,12 +64,6 @@ public class Goto extends Instruction implements JumpingInstruction
out.writeShort(offset);
}
@Override
public void buildJumpGraph()
{
this.addJump(to);
}
@Override
public void execute(Frame frame)

View File

@@ -42,12 +42,6 @@ public class GotoW extends Instruction implements JumpingInstruction
super.write(out);
out.writeInt(to.getPc() - this.getPc());
}
@Override
public void buildJumpGraph()
{
this.addJump(to);
}
@Override
public void execute(Frame frame)

View File

@@ -23,7 +23,6 @@ import net.runelite.asm.attributes.code.instruction.types.PushConstantInstructio
import net.runelite.deob.deobfuscators.rename.MappingExecutorUtil;
import net.runelite.deob.deobfuscators.rename.PacketHandler;
import net.runelite.deob.deobfuscators.rename.ParallelExecutorMapping;
import net.runelite.asm.execution.Execution;
public abstract class If extends Instruction implements JumpingInstruction, ComparisonInstruction, MappableInstruction
{
@@ -71,12 +70,6 @@ public abstract class If extends Instruction implements JumpingInstruction, Comp
super.write(out);
out.writeShort(to.getPc() - this.getPc());
}
@Override
public void buildJumpGraph()
{
this.addJump(to);
}
@Override
public void execute(Frame frame)

View File

@@ -63,12 +63,6 @@ public abstract class If0 extends Instruction implements JumpingInstruction, Com
out.writeShort(to.getPc() - this.getPc());
}
@Override
public void buildJumpGraph()
{
this.addJump(to);
}
@Override
public void execute(Frame frame)
{

View File

@@ -13,7 +13,6 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@@ -101,14 +100,6 @@ public class LookupSwitch extends Instruction implements JumpingInstruction
}
}
@Override
public void buildJumpGraph()
{
for (Instruction i : branchi)
this.addJump(i);
this.addJump(defi);
}
@Override
public void execute(Frame frame)
{

View File

@@ -97,14 +97,6 @@ public class TableSwitch extends Instruction implements JumpingInstruction
out.writeInt(i.getPc() - this.getPc());
}
@Override
public void buildJumpGraph()
{
for (Instruction i : branchi)
this.addJump(i);
this.addJump(defi);
}
@Override
public void execute(Frame frame)
{

View File

@@ -6,7 +6,6 @@ import java.io.IOException;
import net.runelite.deob.deobfuscators.ConstantParameter;
import net.runelite.deob.deobfuscators.FieldInliner;
import net.runelite.deob.deobfuscators.IllegalStateExceptions;
import net.runelite.deob.deobfuscators.MethodInliner;
import net.runelite.deob.deobfuscators.RenameUnique;
import net.runelite.deob.deobfuscators.RuntimeExceptions;
import net.runelite.deob.deobfuscators.UnreachedCode;
@@ -73,25 +72,25 @@ public class Deob
run(group, new UnusedClass());
ModArith mod = new ModArith();
mod.run(group);
int last = -1, cur;
while ((cur = mod.runOnce()) > 0)
{
new MultiplicationDeobfuscator().run(group);
new MultiplyOneDeobfuscator().run(group);
new MultiplyZeroDeobfuscator().run(group);
if (last == cur)
break;
last = cur;
}
mod.annotateEncryption();
// ModArith mod = new ModArith();
// mod.run(group);
//
// int last = -1, cur;
// while ((cur = mod.runOnce()) > 0)
// {
// new MultiplicationDeobfuscator().run(group);
//
// new MultiplyOneDeobfuscator().run(group);
//
// new MultiplyZeroDeobfuscator().run(group);
//
// if (last == cur)
// break;
//
// last = cur;
// }
//
// mod.annotateEncryption();
JarUtil.saveJar(group, new File(args[1]));

View File

@@ -459,7 +459,6 @@ public class ConstantParameter implements Deobfuscator
continue; // ins already removed?
Instructions instructions = ins.getInstructions();
instructions.buildJumpGraph();
// remove the if
if (ctx.getInstruction() instanceof If)
@@ -489,14 +488,8 @@ public class ConstantParameter implements Deobfuscator
assert instructions.getInstructions().contains(to);
// move things that jump here to instead jump to 'to'
for (Instruction fromI : ins.from)
{
assert fromI.jump.contains(ins);
fromI.jump.remove(ins);
fromI.replace(ins, to);
}
ins.from.clear();
for (Instruction i : instructions.getInstructions())
i.replace(ins, to);
instructions.remove(ins);
@@ -505,8 +498,6 @@ public class ConstantParameter implements Deobfuscator
if (branch)
{
Goto gotoins = new Goto(instructions, to);
to.from.add(gotoins);
gotoins.jump.add(to);
// insert goto
instructions.getInstructions().add(idx, gotoins);

View File

@@ -101,22 +101,12 @@ public class FieldInliner implements Deobfuscator
// nop
NOP nop1 = new NOP(instructions), nop2 = new NOP(instructions);
for (Instruction i : prev.from)
{
i.jump.remove(prev);
i.jump.add(nop1);
for (Instruction i : instructions.getInstructions())
i.replace(prev, nop1);
}
prev.from.clear();
for (Instruction i : ins.from)
{
i.jump.remove(ins);
i.jump.add(nop1);
for (Instruction i : instructions.getInstructions())
i.replace(ins, nop1);
}
ins.from.clear();
boolean b = instructions.getInstructions().remove(prev);
assert b;
@@ -144,8 +134,6 @@ public class FieldInliner implements Deobfuscator
// remove fin, add push constant
Instruction i = (Instruction) fin;
i.getInstructions().buildJumpGraph();
Instruction pushIns = new LDC_W(i.getInstructions(), value.getValue());
List<Instruction> instructions = i.getInstructions().getInstructions();
@@ -154,13 +142,8 @@ public class FieldInliner implements Deobfuscator
assert idx != -1;
// move jumps to i to pushIns
for (Instruction i2 : i.from)
{
i2.jump.remove(i);
i2.jump.add(pushIns);
for (Instruction i2 : instructions)
i2.replace(i, pushIns);
}
i.from.clear();
i.getInstructions().remove(i);
instructions.add(idx, pushIns);

View File

@@ -1,339 +0,0 @@
package net.runelite.deob.deobfuscators;
import java.util.ArrayList;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.Deobfuscator;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.instruction.types.FieldInstruction;
import net.runelite.asm.pool.NameAndType;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import net.runelite.asm.attributes.Attributes;
import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.Goto;
import net.runelite.asm.attributes.code.instructions.PutStatic;
import net.runelite.asm.attributes.code.instructions.VReturn;
import net.runelite.asm.execution.Execution;
import net.runelite.asm.execution.Frame;
import net.runelite.asm.execution.InstructionContext;
import net.runelite.asm.execution.StackContext;
import net.runelite.asm.signature.Signature;
import org.apache.commons.collections4.map.MultiValueMap;
public class FieldMover implements Deobfuscator
{
private static final String mainClass = "client";
private Execution execution;
private ClassGroup group;
private MultiValueMap<Field, ClassFile> fields = new MultiValueMap<>();
private Map<Field, PutStatic> clinits = new HashMap<>();
private void findUses()
{
fields.clear();
for (ClassFile cf : group.getClasses())
{
for (Method m : cf.getMethods().getMethods())
{
Code code = m.getCode();
if (code == null)
continue;
for (Instruction i : code.getInstructions().getInstructions())
{
if (!(i instanceof FieldInstruction))
continue;
FieldInstruction fi = (FieldInstruction) i;
Field field = fi.getMyField();
if (field == null)
continue;
if (!field.isStatic())
continue;
if (m.getName().equals("<clinit>"))
{
if (fi instanceof PutStatic)
clinits.put(field, (PutStatic) fi);
}
else if (!m.isStatic()) // I think non static methods are always right?
{
if (fields.containsKey(field))
{
Collection<ClassFile> col = fields.getCollection(field);
if (!col.contains(cf))
fields.put(field, cf);
}
else
fields.put(field, cf);
}
}
}
}
}
private boolean isDowncastable(ClassFile from, ClassFile to)
{
while (from != null && from != to)
{
from = from.getParent();
}
return from != null;
}
private ClassFile getBase(ClassFile one, ClassFile two)
{
if (one == two)
return one;
if (isDowncastable(one, two))
return two;
if (isDowncastable(two, one))
return one;
return null;
}
private ClassFile findCommonBase(Collection<ClassFile> classes)
{
List<ClassFile> list = new ArrayList<>(classes);
if (list.size() == 1)
return list.get(0);
ClassFile cf = getBase(list.get(0), list.get(1));
for (int i = 2; i < list.size(); ++i)
cf = getBase(cf, list.get(i));
return cf;
}
private int moveFields()
{
int count = 0;
for (Field field : fields.keySet())
{
Collection<ClassFile> cfs = fields.getCollection(field);
ClassFile to = findCommonBase(cfs);
if (to == null)
// no common base, move to entry class
to = group.findClass(mainClass);
assert to != null;
if (field.getFields().getClassFile() == to)
continue;
moveField(field, to);
++count;
}
return count;
}
private void moveField(Field field, ClassFile to)
{
assert field.getFields().getClassFile() != to;
net.runelite.asm.pool.Field newField = new net.runelite.asm.pool.Field(
new net.runelite.asm.pool.Class(to.getName()),
new NameAndType(field.getName(), field.getType())
);
// move on instructions
for (ClassFile cf : group.getClasses())
{
for (Method m : cf.getMethods().getMethods())
{
Code code = m.getCode();
if (code == null)
continue;
//code.getInstructions().renameField(field, newField);
}
}
// move the field
field.getFields().getFields().remove(field);
to.getFields().getFields().add(field);
field.setFields(to.getFields());
// move initializer
PutStatic setField = clinits.get(field);
if (setField == null)
return; // no initializer
Method toClinit = to.findMethod("<clinit>");
if (toClinit == null)
{
// make clinit
Signature sig = new Signature("()V");
toClinit = new Method(to.getMethods(), "<clinit>", sig);
Attributes attributes = toClinit.getAttributes();
Code code = new Code(attributes);
attributes.addAttribute(code);
// make instructions
Instructions instructions = new Instructions(code);
code.setInstructions(instructions);
instructions.getInstructions().add(new VReturn(instructions, InstructionType.RETURN, 0)); // add return
to.getMethods().getMethods().add(toClinit);
}
moveInitializer(setField, toClinit);
}
private void moveInitializer(PutStatic setInstruction, Method to)
{
// find instruction in execution and remove it
InstructionContext setCtx = null;
Frame frame = null;
List<StackContext> ctxs = null;
for (Frame f : execution.processedFrames)
for (InstructionContext ctx : f.getInstructions())
{
if (ctx.getInstruction() != setInstruction)
continue;
setCtx = ctx;
// get instructions before recursive stack removal
//List<Instruction> oldIns = new ArrayList<>(frame.getMethod().getCode().getInstructions().getInstructions());
ctxs = ctx.removeStack(0); //remove
frame = f;
//List<Instruction> newIns = new ArrayList<>(frame.getMethod().getCode().getInstructions().getInstructions());
//changedIns = CollectionUtils.disjunction(oldIns, newIns);
break;
}
if (setCtx == null)
{
System.err.println("Unable to locate context for putstatic when moving field initializer");
return;
}
// insert instructions into method
// convert stack info to instruction ctx
List<InstructionContext> ictxs = getContexts(setCtx);
// order instructions based on the order they execute in the frame
Map<Integer, Instruction> orderedIns = new TreeMap<>();
for (InstructionContext i : ictxs)
{
assert frame.getInstructions().indexOf(i) != -1;
orderedIns.put(frame.getInstructions().indexOf(i), i.getInstruction());
}
to.getCode().getInstructions().buildJumpGraph();
frame.getMethod().getCode().getInstructions().buildJumpGraph();
for (Instruction i : orderedIns.values())
{
moveJumps(i);
i.getInstructions().remove(i);
i.setInstructions(to.getCode().getInstructions());
}
// insert instructions into method
to.getCode().getInstructions().getInstructions().addAll(0, orderedIns.values());
}
private void moveJumps(Instruction i)
{
List<Instruction> list = i.getInstructions().getInstructions();
int idx = list.indexOf(i);
Instruction next = list.get(idx + 1);
for (Instruction i2 : i.from)
{
i2.jump.remove(i);
i2.replace(i, next);
next.from.add(i2);
i2.jump.add(next);
}
i.from.clear();
}
private void getContexts(List<InstructionContext> list, InstructionContext ctx)
{
assert !(ctx.getInstruction() instanceof Goto);
if (list.contains(ctx))
return;
list.add(ctx);
for (StackContext s : ctx.getPops())
{
assert s.getPopped() == ctx;
getContexts(list, s.getPushed());
}
for (StackContext s : ctx.getPushes())
{
assert s.getPushed() == ctx;
for (InstructionContext i : s.getPopped())
getContexts(list, i);
}
}
// get instruction contexts for stack contexts
private List<InstructionContext> getContexts(InstructionContext ctx)
{
List<InstructionContext> list = new ArrayList<>();
getContexts(list, ctx);
return list;
}
@Override
public void run(ClassGroup group)
{
group.buildClassGraph();
execution = new Execution(group);
execution.populateInitialMethods();
execution.run();
this.group = group;
findUses();
int count = moveFields();
System.out.println("Moved " + count + " fields");
}
}

View File

@@ -35,7 +35,6 @@ public class IllegalStateExceptions implements Deobfuscator
continue;
Instructions instructions = c.getInstructions();
instructions.buildJumpGraph();
List<Instruction> ilist = instructions.getInstructions();
for (int i = 0; i < ilist.size(); ++i)
@@ -78,17 +77,9 @@ public class IllegalStateExceptions implements Deobfuscator
while (!(ins instanceof AThrow))
{
// modify instructions which jump to here to instead jump to 'to'
for (Instruction from : ins.from)
{
from.jump.remove(ins);
//ins.from.remove(from);
for (Instruction from : ilist)
from.replace(ins, to);
from.jump.add(to);
}
ins.from.clear();
instructions.remove(ins);
ins = ilist.get(i); // don't need to ++i because
@@ -100,8 +91,6 @@ public class IllegalStateExceptions implements Deobfuscator
// insert goto
assert ilist.contains(to);
Goto g = new Goto(instructions, to);
g.jump.add(to);
to.from.add(g);
ilist.add(i, g);
++count;

View File

@@ -1,404 +0,0 @@
package net.runelite.deob.deobfuscators;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.Deobfuscator;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instruction.types.InvokeInstruction;
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
import net.runelite.asm.attributes.code.instruction.types.ReturnInstruction;
import net.runelite.asm.attributes.code.instructions.AStore;
import net.runelite.asm.attributes.code.instructions.DStore;
import net.runelite.asm.attributes.code.instructions.FStore;
import net.runelite.asm.attributes.code.instructions.Goto;
import net.runelite.asm.attributes.code.instructions.IStore;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.LStore;
import net.runelite.asm.attributes.code.instructions.NOP;
import net.runelite.asm.signature.Signature;
import net.runelite.asm.signature.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.runelite.asm.attributes.code.Exceptions;
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
import net.runelite.asm.attributes.code.instructions.If;
public class MethodInliner implements Deobfuscator
{
private Map<Method, Integer> calls = new HashMap<>();
//private Set<Method> removeMethods = new HashSet<>();
private void countCalls(Method m)
{
Code code = m.getCode();
if (code == null)
return;
Instructions ins = code.getInstructions();
for (Instruction i : ins.getInstructions())
{
// can only inline static method calls
if (!(i instanceof InvokeStatic))
continue;
List<Method> invokedMethods = ((InvokeInstruction) i).getMethods();
if (invokedMethods.isEmpty())
continue; // not our method
assert invokedMethods.size() == 1;
Method invokedMethod = invokedMethods.get(0);
Integer count = calls.get(invokedMethod);
if (count == null)
calls.put(invokedMethod, 1);
else
calls.put(invokedMethod, count + 1);
}
}
private int processMethod(Method m)
{
int inlineCount = 0;
Code code = m.getCode();
if (code == null)
return inlineCount;
Instructions ins = code.getInstructions();
for (Instruction i : ins.getInstructions())
{
assert i.getInstructions() == ins;
// can only inline static method calls
if (!(i instanceof InvokeStatic))
continue;
List<Method> invokedMethods = ((InvokeInstruction) i).getMethods();
if (invokedMethods.isEmpty())
continue; // not our method
Method invokedMethod = invokedMethods.get(0);
Integer count = calls.get(invokedMethod);
// this can't inline functions with exception handlers because throwing exceptions clears the stack
if (count == null
|| !invokedMethod.getCode().getExceptions().getExceptions().isEmpty()
|| invokedMethod.getCode().getInstructions().getInstructions().size() > 30) // XXX magic
continue;
// if (count == null || count != 1)
// continue; // only inline methods called once
if (m == invokedMethod)
continue; // recursive method
int invokeIdx = ins.getInstructions().indexOf(i);
assert invokeIdx != -1;
assert ins.getInstructions().get(invokeIdx).getInstructions() == ins;
int lvtIndex = code.getMaxLocals(),
theirLocals = invokedMethod.getCode().getMaxLocals();
if (lvtIndex + theirLocals > 127)
continue;
if (invokedMethod.isSynchronized())
continue;
// assign variables on stack to lvt
Signature descriptor = invokedMethod.getDescriptor();
Map<Integer, Integer> lvtIndexes = new HashMap<>();
for (int j = 0, idx = 0; j < descriptor.size(); ++j)
{
lvtIndexes.put(j, idx);
idx += descriptor.getTypeOfArg(j).getSlots();
}
Instruction firstParamStore = null;
for (int j = descriptor.size() - 1; j >= 0; --j)
{
Type type = descriptor.getTypeOfArg(j);
int paramLvtIndex = lvtIndexes.get(j);
// insert instruction to store top of stack in lvt
Instruction storeIns = null;
if (type.getArrayDims() == 0)
{
switch (type.getType())
{
case "B":
case "Z":
case "C":
case "S":
case "I":
storeIns = new IStore(ins, lvtIndex + paramLvtIndex);
break;
case "J":
storeIns = new LStore(ins, lvtIndex + paramLvtIndex);
break;
case "F":
storeIns = new FStore(ins, lvtIndex + paramLvtIndex);
break;
case "D":
storeIns = new DStore(ins, lvtIndex + paramLvtIndex);
break;
}
}
if (type.getArrayDims() != 0 || type.getType().startsWith("L"))
{
assert storeIns == null;
storeIns = new AStore(ins, lvtIndex + paramLvtIndex);
}
assert storeIns != null;
// insert storeIns before invoke instruction
ins.getInstructions().add(invokeIdx++, storeIns);
if (firstParamStore == null)
firstParamStore = storeIns;
}
int maxStack = code.getMaxStack() + invokedMethod.getCode().getMaxStack(); // not really right but ok
code.setMaxStack(maxStack);
inline(m, i, invokedMethod, lvtIndex, firstParamStore);
++inlineCount;
break;
}
return inlineCount;
}
private void inline(Method method, Instruction invokeIns, Method invokeMethod, int lvtBase, Instruction firstParamStore)
{
Code methodCode = method.getCode(),
invokeMethodCode = invokeMethod.getCode();
Instructions methodInstructions = methodCode.getInstructions(),
invokeMethodInstructions = invokeMethodCode.getInstructions();
int idx = methodInstructions.getInstructions().indexOf(invokeIns); // index of invoke ins, before removal
assert invokeIns.getInstructions() == methodInstructions;
assert idx != -1;
Instruction nextInstruction = methodInstructions.getInstructions().get(idx + 1);
// move stuff which jumps to invokeIns to firstParamStore. If there are no arguments that are stored,
// firstParamStore is null, and so create a nop instruction.
if (firstParamStore == null)
{
Instruction nop = new NOP(methodInstructions);
methodInstructions.getInstructions().add(idx + 1, nop);
++idx;
firstParamStore = nop;
}
methodInstructions.buildJumpGraph();
invokeMethodInstructions.buildJumpGraph();
for (Instruction fromI : invokeIns.from)
{
assert fromI.jump.contains(invokeIns);
fromI.jump.remove(invokeIns);
fromI.replace(invokeIns, firstParamStore);
fromI.jump.add(firstParamStore);
firstParamStore.from.add(fromI);
}
invokeIns.from.clear();
for (net.runelite.asm.attributes.code.Exception e : invokeMethodCode.getExceptions().getExceptions())
e.replace(invokeIns, firstParamStore);
methodInstructions.remove(invokeIns);
Map<Instruction, Instruction> insMap = new HashMap<>();
for (Instruction i : invokeMethodInstructions.getInstructions())
{
Instruction i2 = i.clone();
i2.setInstructions(null);
insMap.put(i, i2);
}
for (Instruction i : insMap.values())
{
for (Entry<Instruction, Instruction> e : insMap.entrySet())
{
i.replace(e.getKey(), e.getValue());
}
}
for (Instruction i : insMap.values())
{
if (i instanceof JumpingInstruction)
{
JumpingInstruction j = (JumpingInstruction) i;
for (Instruction i2 : j.getJumps())
{
assert insMap.values().contains(i2);
assert i2.getInstructions() == null;
}
}
}
Exceptions fromExceptions = invokeMethod.getCode().getExceptions();
Exceptions toExceptions = method.getCode().getExceptions();
for (net.runelite.asm.attributes.code.Exception e : fromExceptions.getExceptions())
{
e = e.clone();
e.setExceptions(toExceptions);
for (Entry<Instruction, Instruction> en : insMap.entrySet())
{
e.replace(en.getKey(), en.getValue());
}
toExceptions.add(e);
}
for (Instruction i : invokeMethodInstructions.getInstructions())
{
Instruction orig = i;
i = insMap.get(i);
// copy instructions over.
if (i instanceof ReturnInstruction)
{
// XXX I am assuming that this function leaves the stack in a clean state?
// instead of return, jump to next instruction after the invoke
Instruction oldI = i;
assert oldI.getInstructions() == null;
i = new Goto(methodInstructions, nextInstruction);
assert nextInstruction.getInstructions() == methodInstructions;
assert methodInstructions.getInstructions().contains(nextInstruction);
for (Instruction i2 : insMap.values())
i2.replace(oldI, i);
for (net.runelite.asm.attributes.code.Exception e : toExceptions.getExceptions())
e.replace(oldI, i);
insMap.put(orig, i);
}
if (i instanceof LVTInstruction)
{
LVTInstruction lvt = (LVTInstruction) i;
// offset lvt index
int newIndex = lvtBase + lvt.getVariableIndex();
Instruction oldI = i;
i = lvt.setVariableIndex(newIndex);
assert oldI == i;
// if (oldI != i)
// {
// for (Instruction i2 : insMap.values())
// i2.replace(oldI, i);
//
// for (net.runelite.deob.attributes.code.Exception e : toExceptions.getExceptions())
// e.replace(oldI, i);
//
// insMap.put(orig, i);
// }
}
i.setInstructions(methodInstructions);
assert !methodInstructions.getInstructions().contains(i);
methodInstructions.getInstructions().add(idx++, i);
}
this.checkJumpGraph(methodInstructions);
}
private void checkJumpGraph(Instructions ins)
{
ins.buildJumpGraph();
assert new HashSet<>(ins.getInstructions()).size() == ins.getInstructions().size();
for (Instruction i : ins.getInstructions())
{
for (Instruction i2 : i.jump)
{
assert i2.getInstructions() == ins;
assert ins.getInstructions().contains(i2);
assert i2.from.contains(i);
}
for (Instruction i2 : i.from)
{
assert i2.getInstructions() == ins;
assert ins.getInstructions().contains(i2);
assert i2.jump.contains(i);
}
if (i instanceof JumpingInstruction)
{
JumpingInstruction j = (JumpingInstruction) i;
assert j.getJumps().size() == i.jump.size();
}
}
}
@Override
public void run(ClassGroup group)
{
int total = 0;
int i;
do
{
i = pass(group);
total += i;
}
while (i > 0);
System.out.println("[TOTAL] Inlined " + total + " methods");
}
private int pass(ClassGroup group)
{
group.buildClassGraph();
int count = 0;
calls.clear();
for (ClassFile cf : group.getClasses())
{
for (Method m : cf.getMethods().getMethods())
{
countCalls(m);
}
}
for (ClassFile cf : group.getClasses())
{
for (Method m : cf.getMethods().getMethods())
{
count += processMethod(m);
}
}
System.out.println("Inlined " + count + " methods");
return count;
}
}

View File

@@ -27,10 +27,6 @@ public class UnreachedCode implements Deobfuscator
if (!execution.executed.contains(i))
{
for (Instruction i2 : i.from)
i2.jump.remove(i);
i.from.clear(); // if this is never executed, anything that jumps here ia also never executed?
// if this is an exception handler, the exception handler is never used...
for (net.runelite.asm.attributes.code.Exception e : new ArrayList<>(m.getCode().getExceptions().getExceptions()))
{