diff --git a/jode/jode/obfuscator/LocalOptimizer.java b/jode/jode/obfuscator/LocalOptimizer.java index b0c1846..455b81a 100644 --- a/jode/jode/obfuscator/LocalOptimizer.java +++ b/jode/jode/obfuscator/LocalOptimizer.java @@ -1,8 +1,28 @@ +/* LocalOptimizer Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + package jode.obfuscator; import java.util.*; import jode.bytecode.*; +import jode.AssertError; -public class LocalOptimizer { +public class LocalOptimizer implements Opcodes { class LocalInfo { LocalInfo shadow = null; @@ -16,6 +36,7 @@ public class LocalOptimizer { Vector usingInstrs = new Vector(); Vector conflictingLocals = new Vector(); + int size; int newSlot = -1; LocalInfo(InstrInfo instr) { @@ -51,17 +72,18 @@ public class LocalOptimizer { } } } - + class InstrInfo { - /** - * Tell if the localsToRead variable has changed. - */ - boolean changed; /** * The LocalInfo of the next Instruction, that may read a local, * without prior writing. */ LocalInfo[] nextReads; + /** + * The jsr to which the nextReads at this slot belongs to. + * I think I don't like jsrs any more. + */ + Vector[] belongsToJsrs; /** * The LocalInfo for this local */ @@ -74,9 +96,14 @@ public class LocalOptimizer { * The next info in the chain. */ InstrInfo nextInfo; + /** + * If instruction is a jsr, this array contains the ret instructions. + */ + Instruction[] retInstrs; } InstrInfo firstInfo; + Stack changedInfos; Hashtable instrInfos; BytecodeInfo bc; @@ -84,12 +111,63 @@ public class LocalOptimizer { this.bc = bc; } - public void promoteReads(InstrInfo info, Instruction preInstr) { + /** + * This method determines which rets belong to a given jsr. This + * is needed, since the predecessors must be exact. + */ + void findRets(InstrInfo jsrInfo) { + Vector rets = new Vector(); + Stack instrStack = new Stack(); + Instruction subInstr = jsrInfo.instr.succs[0]; + if (subInstr.opcode != opc_astore) + throw new AssertError("Non standard jsr"); + int slot = subInstr.localSlot; + instrStack.push(subInstr.nextByAddr); + while (!instrStack.isEmpty()) { + Instruction instr = (Instruction) instrStack.pop(); + if (instr.localSlot == slot) { + if (instr.opcode != opc_ret) + throw new AssertError("Non standard jsr"); + rets.addElement(instr); + } + if (!instr.alwaysJumps) + instrStack.push(instr.nextByAddr); + if (instr.succs != null) + for (int i=0; i< instr.succs.length; i++) + instrStack.push(instr.succs[i]); + } + jsrInfo.retInstrs = new Instruction[rets.size()]; + rets.copyInto(jsrInfo.retInstrs); + } + + /** + * Merges the given vector to a new vector. Both vectors may + * be null in which case they are interpreted as empty vectors. + * The vectors will never changed, but the result may be one + * of the given vectors. + */ + Vector merge(Vector v1, Vector v2) { + if (v1 == null || v1.isEmpty()) + return v2; + if (v2 == null || v2.isEmpty()) + return v1; + Vector result = (Vector) v1.clone(); + Enumeration enum = v2.elements(); + while (enum.hasMoreElements()) { + Object elem = enum.nextElement(); + if (!result.contains(elem)) + result.addElement(elem); + } + return result; + } + + void promoteReads(InstrInfo info, Instruction preInstr, + Instruction belongingJsr) { InstrInfo preInfo = (InstrInfo) instrInfos.get(preInstr); int omitLocal = -1; if (preInstr.localSlot != -1 - && preInstr.opcode >= BytecodeInfo.opc_istore - && preInstr.opcode < BytecodeInfo.opc_iastore) { + && preInstr.opcode >= opc_istore + && preInstr.opcode <= opc_astore) { /* This is a store */ omitLocal = preInstr.localSlot; if (info.nextReads[preInstr.localSlot] != null) @@ -98,10 +176,30 @@ public class LocalOptimizer { } int maxlocals = bc.getMaxLocals(); for (int i=0; i< maxlocals; i++) { + Vector newBelongs = info.belongsToJsrs[i]; + if (preInstr.opcode == opc_jsr + && newBelongs != null) { + if (!info.belongsToJsrs[i].contains(preInstr)) + /* This was the wrong jsr */ + continue; + if (info.belongsToJsrs[i].size() == 1) + newBelongs = null; + else { + newBelongs = (Vector) info.belongsToJsrs[i].clone(); + newBelongs.removeElement(preInstr); + } + } + if (belongingJsr != null) { + if (newBelongs == null) + newBelongs = new Vector(); + newBelongs.addElement(belongingJsr); + } if (info.nextReads[i] != null && i != omitLocal) { + preInfo.belongsToJsrs[i] + = merge(preInfo.belongsToJsrs[i], newBelongs); if (preInfo.nextReads[i] == null) { preInfo.nextReads[i] = info.nextReads[i]; - preInfo.changed = true; + changedInfos.push(preInfo); } else { preInfo.nextReads[i] .combineInto(info.nextReads[i]); @@ -115,6 +213,7 @@ public class LocalOptimizer { Handler[] handlers = bc.getExceptionHandlers(); /* Initialize the InstrInfos and LocalInfos */ + changedInfos = new Stack(); instrInfos = new Hashtable(); { InstrInfo info = firstInfo = new InstrInfo(); @@ -123,13 +222,25 @@ public class LocalOptimizer { instrInfos.put(instr, info); info.instr = instr; info.nextReads = new LocalInfo[maxlocals]; + info.belongsToJsrs = new Vector[maxlocals]; if (instr.localSlot != -1) { info.local = new LocalInfo(info); - if (instr.opcode < BytecodeInfo.opc_istore - || instr.opcode > BytecodeInfo.opc_iastore) { + info.local.size = 1; + switch (instr.opcode) { + case opc_lload: case opc_dload: + info.local.size = 2; + /* fall through */ + case opc_iload: case opc_fload: case opc_aload: + case opc_ret: + case opc_iinc: /* this is a load instruction */ info.nextReads[instr.localSlot] = info.local; - info.changed = true; + changedInfos.push(info); + break; + + case opc_lstore: case opc_dstore: + info.local.size = 2; + case opc_istore: case opc_fstore: case opc_astore: } } if ((instr = instr.nextByAddr) == null) @@ -138,54 +249,71 @@ public class LocalOptimizer { } } + for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) + if (info.instr.opcode == opc_jsr) + findRets(info); + /* find out which locals are the same. */ - boolean changed = true; - while (changed) { - changed = false; - for (InstrInfo info = firstInfo; - info != null; info = info.nextInfo) { - if (info.changed) { - info.changed = false; - Enumeration enum = info.instr.preds.elements(); - while (enum.hasMoreElements()) { - changed = true; - Instruction preInstr - = (Instruction) enum.nextElement(); - promoteReads(info, preInstr); - } - for (int i=0; i 0; ) + promoteReads(info, jsrInfo.retInstrs[i], preInstr); + } else + promoteReads(info, preInstr, null); + } + for (int i=0; i= BytecodeInfo.opc_istore - && info.instr.opcode < BytecodeInfo.opc_iastore) { - /* This is a store. It conflicts with every local, whose - * value will be read without write. - */ - for (int i=0; i < maxlocals; i++) { - if (i != info.instr.localSlot - && info.nextReads[i] != null) - info.local.conflictsWith(info.nextReads[i]); + if (info.local != null && info.local.usingInstrs.size() == 1) { + /* If this is a store, whose value is never read; it can + * be removed, i.e replaced by a pop. */ + switch (info.instr.opcode) { + case opc_istore: + case opc_fstore: + case opc_astore: + info.local = null; + info.instr.opcode = opc_pop; + info.instr.length = 1; + info.instr.localSlot = -1; + break; + case opc_lstore: + case opc_dstore: + info.local = null; + info.instr.opcode = opc_pop2; + info.instr.length = 1; + info.instr.localSlot = -1; + break; + default: } } } } - public void distributeLocals(Vector locals) { + void distributeLocals(Vector locals) { if (locals.size() == 0) return; @@ -217,8 +345,18 @@ public class LocalOptimizer { for (int slot = 0; ; slot++) { Enumeration conflenum = bestLocal.conflictingLocals.elements(); while (conflenum.hasMoreElements()) { - if (((LocalInfo)conflenum.nextElement()).newSlot == slot) + LocalInfo conflLocal = (LocalInfo)conflenum.nextElement(); + if (bestLocal.size == 2 && conflLocal.newSlot == slot+1) { + slot++; + continue next_slot; + } + if (conflLocal.size == 2 && conflLocal.newSlot+1 == slot) + continue next_slot; + if (conflLocal.newSlot == slot) { + if (conflLocal.size == 2) + slot++; continue next_slot; + } } bestLocal.newSlot = slot; break; @@ -241,6 +379,23 @@ public class LocalOptimizer { firstInfo.nextReads[i].getReal().newSlot = i; } + /* Now calculate the conflict settings. + */ + for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { + if (info.instr.localSlot != -1 + && info.instr.opcode >= BytecodeInfo.opc_istore + && info.instr.opcode <= BytecodeInfo.opc_astore) { + /* This is a store. It conflicts with every local, whose + * value will be read without write. + */ + for (int i=0; i < maxlocals; i++) { + if (i != info.instr.localSlot + && info.nextReads[i] != null) + info.local.conflictsWith(info.nextReads[i]); + } + } + } + /* Now put the locals that need a color into a vector. */ Vector locals = new Vector();