diff --git a/jode/jode/bytecode/Reference.java b/jode/jode/bytecode/Reference.java new file mode 100644 index 0000000..7b888a8 --- /dev/null +++ b/jode/jode/bytecode/Reference.java @@ -0,0 +1,89 @@ +/* Reference 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.bytecode; +import java.util.Vector; +import java.util.Enumeration; +import jode.Type; + +/** + * This class represents a field or method reference + * + * For simplicity currently most fields are public. You shouldn't change + * many of them, though. + */ +public class Reference { + /** + * The class info. + */ + String className; + /** + * The member name. Don't make this a MethodInfo, since the clazz + * may not be readable. + */ + String memberName; + /** + * The member type. + */ + String memberType; + + public Reference(String className, String name, String type) { + this.className = className; + this.memberName = name; + this.memberType = type; + } + + public String getClazz() { + return className; + } + + public String getName() { + return memberName; + } + + public String getType() { + return memberType; + } + + public void setClazz(String name) { + className = name; + } + + public void setName(String name) { + memberName = name; + } + + public void setType(String type) { + memberType = type; + } + + public String toString() { + return className + "." + memberName + "." + memberType; + } + + public boolean equals(Object o) { + if (o instanceof Reference) { + Reference other = (Reference) o; + return other.className.equals(className) + && other.memberName.equals(memberName) + && other.memberType.equals(memberType); + } + return false; + } +} diff --git a/jode/jode/obfuscator/CodeAnalyzer.java b/jode/jode/obfuscator/CodeAnalyzer.java new file mode 100644 index 0000000..95a7412 --- /dev/null +++ b/jode/jode/obfuscator/CodeAnalyzer.java @@ -0,0 +1,26 @@ +/* CodeAnalyzer 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 jode.bytecode.BytecodeInfo; + +public interface CodeAnalyzer { + public void analyzeCode(); + public BytecodeInfo stripCode(); +} diff --git a/jode/jode/obfuscator/ConstantAnalyzer.java b/jode/jode/obfuscator/ConstantAnalyzer.java new file mode 100644 index 0000000..5f4ac44 --- /dev/null +++ b/jode/jode/obfuscator/ConstantAnalyzer.java @@ -0,0 +1,1267 @@ +/* ConstantAnalyzer 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 jode.MethodType; +import jode.Type; +import jode.bytecode.*; +import java.util.*; +import java.lang.reflect.Array; +import java.lang.reflect.Modifier; + +interface ConstantListener { + public void constantChanged(); +} + +class ConstantAnalyzerValue implements ConstantListener { + public final static Object VOLATILE = new Object(); + /** + * The constant value, VOLATILE if value is not constant. + */ + Object value; + /** + * The number of slots this value takes on the stack. + */ + int stackSize; + /** + * The constant listeners, that want to be informed if this is + * no longer constant. + */ + Vector listeners; + + public ConstantAnalyzerValue(Object constant) { + value = constant; + stackSize = (constant instanceof Double + || constant instanceof Long) ? 2 : 1; + listeners = new Vector(); + } + + public ConstantAnalyzerValue(ConstantAnalyzerValue constant) { + value = constant.value; + stackSize = constant.stackSize; + listeners = new Vector(); + constant.addConstantListener(this); + } + + public ConstantAnalyzerValue(int stackSize) { + this.value = VOLATILE; + this.stackSize = stackSize; + } + + public ConstantAnalyzerValue copy() { + return (value == VOLATILE) ? this : new ConstantAnalyzerValue(this); + } + + public void addConstantListener(ConstantListener l) { + if (!listeners.contains(l)) + listeners.addElement(l); + } + + public void removeConstantListener(ConstantListener l) { + listeners.removeElement(l); + } + + public void fireChanged() { + value = VOLATILE; + Enumeration enum = listeners.elements(); + while (enum.hasMoreElements()) + ((ConstantListener) enum.nextElement()).constantChanged(); + listeners = null; + } + + public void constantChanged() { + if (value != VOLATILE) + fireChanged(); + } + + /** + * Merge this value with other. + * @return true, if the value changed. + */ + public void merge(ConstantAnalyzerValue other) { + if (value == other.value) + return; + if (value != null && value.equals(other.value)) + return; + + if (value != VOLATILE) + fireChanged(); + if (other.value != VOLATILE) + other.fireChanged(); + } +} + + +/** + * Analyze the code, assuming every field that is not yet written to + * is constant. This may imply that some code is dead code. + * + * @author Jochen Hoenicke + */ +public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { + boolean working; + MethodIdentifier m; + BytecodeInfo bytecode; + Hashtable constInfos = null; + Stack instrStack; + + final static int REACHABLE = 0x1; + final static int CONSTANT = 0x2; + final static int CONSTANTFLOW = 0x4; + + class ConstantAnalyzerInfo implements ConstantListener { + ConstantAnalyzerValue[] stack; + ConstantAnalyzerValue[] locals; + Instruction instr; + + public ConstantAnalyzerValue copy(ConstantAnalyzerValue value) { + return (value == null) ? null : value.copy(); + } + + private ConstantAnalyzerInfo(ConstantAnalyzerValue[] stack, + ConstantAnalyzerValue[] locals) { + this.stack = stack; + this.locals = locals; + } + + public ConstantAnalyzerInfo(int numLocals, + boolean isStatic, MethodType mt) { + locals = new ConstantAnalyzerValue[numLocals]; + stack = new ConstantAnalyzerValue[0]; + int slot = 0; + if (!isStatic) + locals[slot++] = new ConstantAnalyzerValue(1); + for (int i=0; i< mt.getParameterTypes().length; i++) { + locals[slot] = new ConstantAnalyzerValue + (mt.getParameterTypes()[i].stackSize()); + slot += locals[slot].stackSize; + } + } + + public void constantChanged() { + if (!instrStack.contains(instr)) + instrStack.push(instr); + } + + public ConstantAnalyzerInfo poppush(int pops, ConstantAnalyzerValue push) { + ConstantAnalyzerValue[] newStack + = new ConstantAnalyzerValue[stack.length - pops + push.stackSize]; + System.arraycopy(stack, 0, newStack, 0, stack.length-pops); + newStack[stack.length-pops] = push.copy(); + return new ConstantAnalyzerInfo(newStack, locals); + } + + public ConstantAnalyzerInfo pop(int pops) { + ConstantAnalyzerValue[] newStack + = new ConstantAnalyzerValue[stack.length - pops]; + System.arraycopy(stack, 0, newStack, 0, stack.length-pops); + return new ConstantAnalyzerInfo(newStack, locals); + } + + public ConstantAnalyzerInfo dup(int count, int depth) { + ConstantAnalyzerValue[] newStack + = new ConstantAnalyzerValue[stack.length + count]; + if (depth == 0) + System.arraycopy(stack, 0, newStack, 0, stack.length); + else { + int pos = stack.length - count - depth; + System.arraycopy(stack, 0, newStack, 0, pos); + for (int i=0; i < count; i++) + newStack[pos++] = copy(stack[stack.length-count + i]); + for (int i=0; i < depth; i++) + newStack[pos++] = copy(stack[stack.length-count-depth + i]); + } + for (int i=0; i < count; i++) + newStack[stack.length+i] = copy(stack[stack.length-count + i]); + return new ConstantAnalyzerInfo(newStack, locals); + } + + public ConstantAnalyzerInfo swap() { + ConstantAnalyzerValue[] newStack + = new ConstantAnalyzerValue[stack.length]; + System.arraycopy(stack, 0, newStack, 0, stack.length - 2); + newStack[stack.length-2] = stack[stack.length-1].copy(); + newStack[stack.length-1] = stack[stack.length-2].copy(); + return new ConstantAnalyzerInfo(newStack, locals); + } + + public ConstantAnalyzerValue getLocal(int slot) { + return locals[slot]; + } + + public ConstantAnalyzerValue getStack(int depth) { + return stack[stack.length - depth]; + } + + public ConstantAnalyzerInfo setLocal(int slot, + ConstantAnalyzerValue value) { + ConstantAnalyzerValue[] newLocals + = (ConstantAnalyzerValue[]) locals.clone(); + newLocals[slot] = value; + if (value.stackSize == 2) + newLocals[slot+1] = null; + return new ConstantAnalyzerInfo(stack, newLocals); + } + + public void merge(ConstantAnalyzerInfo other) { + for (int i=0; i < locals.length; i++) { + if (locals[i] != null) { + if (other.locals[i] == null) { + if (!instrStack.contains(instr)) + instrStack.push(instr); + locals[i] = null; + } else + locals[i].merge(other.locals[i]); + } + } + if (stack.length != other.stack.length) + throw new jode.AssertError("stack length differs"); + for (int i=0; i < stack.length; i++) { + if ((other.stack[i] == null) != (stack[i] == null)) + throw new jode.AssertError("stack types differ"); + else if (stack[i] != null) + stack[i].merge(other.stack[i]); + } + } + } + + class ConstantInfo implements ConstantListener { + int flags; + /** + * The constant, may be an Instruction for CONSTANTFLOW. + */ + Object constant; + + public void constantChanged() { + constant = null; + flags &= ~(CONSTANT | CONSTANTFLOW); + } + } + + static ConstantAnalyzerValue[] unknownValue = { + new ConstantAnalyzerValue(1), new ConstantAnalyzerValue(2) + }; + + public ConstantAnalyzer(BytecodeInfo code, MethodIdentifier method) { + this.bytecode = code; + this.m = method; + } + + public void mergeInfo(Instruction instr, + ConstantAnalyzerInfo info) { + if (instr.tmpInfo == null) { + if (!instrStack.contains(instr)) + instrStack.push(instr); + instr.tmpInfo = info; + info.instr = instr; + } else + ((ConstantAnalyzerInfo)instr.tmpInfo).merge(info); + } + + public void handleReference(Reference ref, boolean isVirtual) { + m.clazz.bundle.reachableIdentifier + (ref.getClazz()+"."+ref.getName()+"."+ref.getType(), isVirtual); + } + + public void handleClass(String clName) { + if (clName.charAt(0) == '[') { + int i; + for (i=0; i< clName.length(); i++) + if (clName.charAt(i) != '[') + break; + if (i >= clName.length() || clName.charAt(i) != 'L') + return; + int index = clName.indexOf(';', i); + if (index != clName.length()-1) + return; + clName = clName.substring(i+1, index); + } + m.clazz.bundle.reachableIdentifier(clName, false); + } + + public void handleOpcode(Instruction instr) { + ConstantAnalyzerInfo info = (ConstantAnalyzerInfo) instr.tmpInfo; + ConstantInfo shortInfo = (ConstantInfo) constInfos.get(instr); + if (shortInfo == null) + constInfos.put(instr, shortInfo = new ConstantInfo()); + shortInfo.flags = REACHABLE; + + int opcode = instr.opcode; + Handler[] handlers = bytecode.getExceptionHandlers(); + for (int i=0; i< handlers.length; i++) { + if (handlers[i].start.addr <= instr.addr + && handlers[i].end.addr >= instr.addr) + mergeInfo(handlers[i].catcher, + info.poppush(info.stack.length, unknownValue[0])); + } + ConstantAnalyzerValue result; + switch (opcode) { + case opc_nop: + mergeInfo(instr.nextByAddr, info.pop(0)); + break; + + case opc_ldc: + case opc_ldc2_w: + result = new ConstantAnalyzerValue(instr.objData); + mergeInfo(instr.nextByAddr, info.poppush(0, result)); + break; + + case opc_iload: case opc_lload: + case opc_fload: case opc_dload: case opc_aload: + result = info.getLocal(instr.localSlot); + if (result.value != ConstantAnalyzerValue.VOLATILE) { + shortInfo.flags |= CONSTANT; + shortInfo.constant = result.value; + result.addConstantListener(shortInfo); + } + mergeInfo(instr.nextByAddr, + info.setLocal(instr.localSlot, result.copy()) + .poppush(0, result)); + break; + case opc_iaload: case opc_laload: + case opc_faload: case opc_daload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: { +// ConstantAnalyzerValue array = info.getStack(2); +// ConstantAnalyzerValue index = info.getStack(1); +// ConstantAnalyzerValue newValue = null; +// if (index.value != index.ConstantAnalyzerValue.VOLATILE +// && array.value != array.ConstantAnalyzerValue.VOLATILE +// && array.value != null) { +// int indexVal = ((Integer) index.value).intValue(); +// Object content; +// switch(opcode) { +// case opc_baload: +// content = new Integer +// (array.value instanceof byte[] +// ? ((byte[])array.value)[indexVal] +// : ((boolean[])array.value)[indexVal] ? 1 : 0); +// case opc_caload: +// content = new Integer(((char[])array.value)[indexVal]); +// break; +// case opc_saload: +// content = new Integer(((short[])array.value)[indexVal]); +// break; +// case opc_iaload: +// case opc_laload: +// case opc_faload: +// case opc_daload: +// case opc_aaload: +// content = Array.get(array.value, indexVal); +// break; +// default: +// throw new jode.AssertError("Can't happen."); +// } +// result = new ConstantAnalyzerValue(content); +// array.addConstantListener(result); +// index.addConstantListener(result); +// } else { + result = unknownValue[(opcode == opc_laload + || opcode == opc_daload) ? 1 : 0]; +// } + mergeInfo(instr.nextByAddr, info.poppush(2, result)); + break; + } + case opc_istore: case opc_fstore: case opc_astore: { + mergeInfo(instr.nextByAddr, + info.pop(1).setLocal(instr.localSlot, info.getStack(1))); + break; + } + case opc_lstore: case opc_dstore: { + mergeInfo(instr.nextByAddr, + info.pop(2).setLocal(instr.localSlot, info.getStack(2))); + break; + } + case opc_iastore: case opc_lastore: + case opc_fastore: case opc_dastore: case opc_aastore: + case opc_bastore: case opc_castore: case opc_sastore: { + int size = (opcode == opc_lastore + || opcode == opc_dastore) ? 2 : 1; + mergeInfo(instr.nextByAddr, info.pop(3)); + break; + } + case opc_pop: case opc_pop2: + mergeInfo(instr.nextByAddr, info.pop(opcode - (opc_pop - 1))); + break; + + case opc_dup: case opc_dup_x1: case opc_dup_x2: + case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: + mergeInfo(instr.nextByAddr, + info.dup((opcode - (opc_dup - 3)) / 3, + (opcode - (opc_dup - 3)) % 3)); + break; + case opc_swap: + mergeInfo(instr.nextByAddr, info.swap()); + break; + + case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: + case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: + case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: + case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: + case opc_irem: case opc_lrem: case opc_frem: case opc_drem: + case opc_iand: case opc_land: + case opc_ior : case opc_lor : + case opc_ixor: case opc_lxor: { + int size = 1 + (opcode - opc_iadd & 1); + ConstantAnalyzerValue value1 = info.getStack(2*size); + ConstantAnalyzerValue value2 = info.getStack(1*size); + if (value1.value != ConstantAnalyzerValue.VOLATILE + && value2.value != ConstantAnalyzerValue.VOLATILE) { + Object newValue; + switch (opcode) { + case opc_iadd: + newValue = new Integer + (((Integer)value1.value).intValue() + + ((Integer)value2.value).intValue()); + break; + case opc_isub: + newValue = new Integer + (((Integer)value1.value).intValue() + - ((Integer)value2.value).intValue()); + break; + case opc_imul: + newValue = new Integer + (((Integer)value1.value).intValue() + * ((Integer)value2.value).intValue()); + break; + case opc_idiv: + newValue = new Integer + (((Integer)value1.value).intValue() + / ((Integer)value2.value).intValue()); + break; + case opc_irem: + newValue = new Integer + (((Integer)value1.value).intValue() + % ((Integer)value2.value).intValue()); + break; + case opc_iand: + newValue = new Integer + (((Integer)value1.value).intValue() + & ((Integer)value2.value).intValue()); + break; + case opc_ior: + newValue = new Integer + (((Integer)value1.value).intValue() + | ((Integer)value2.value).intValue()); + break; + case opc_ixor: + newValue = new Integer + (((Integer)value1.value).intValue() + ^ ((Integer)value2.value).intValue()); + break; + + case opc_ladd: + newValue = new Long + (((Long)value1.value).longValue() + + ((Long)value2.value).longValue()); + break; + case opc_lsub: + newValue = new Long + (((Long)value1.value).longValue() + - ((Long)value2.value).longValue()); + break; + case opc_lmul: + newValue = new Long + (((Long)value1.value).longValue() + * ((Long)value2.value).longValue()); + break; + case opc_ldiv: + newValue = new Long + (((Long)value1.value).longValue() + / ((Long)value2.value).longValue()); + break; + case opc_lrem: + newValue = new Long + (((Long)value1.value).longValue() + % ((Long)value2.value).longValue()); + break; + case opc_land: + newValue = new Long + (((Long)value1.value).longValue() + & ((Long)value2.value).longValue()); + break; + case opc_lor: + newValue = new Long + (((Long)value1.value).longValue() + | ((Long)value2.value).longValue()); + break; + case opc_lxor: + newValue = new Long + (((Long)value1.value).longValue() + ^ ((Long)value2.value).longValue()); + break; + + case opc_fadd: + newValue = new Float + (((Float)value1.value).floatValue() + + ((Float)value2.value).floatValue()); + break; + case opc_fsub: + newValue = new Float + (((Float)value1.value).floatValue() + - ((Float)value2.value).floatValue()); + break; + case opc_fmul: + newValue = new Float + (((Float)value1.value).floatValue() + * ((Float)value2.value).floatValue()); + break; + case opc_fdiv: + newValue = new Float + (((Float)value1.value).floatValue() + / ((Float)value2.value).floatValue()); + break; + case opc_frem: + newValue = new Float + (((Float)value1.value).floatValue() + % ((Float)value2.value).floatValue()); + break; + + case opc_dadd: + newValue = new Double + (((Double)value1.value).doubleValue() + + ((Double)value2.value).doubleValue()); + break; + case opc_dsub: + newValue = new Double + (((Double)value1.value).doubleValue() + - ((Double)value2.value).doubleValue()); + break; + case opc_dmul: + newValue = new Double + (((Double)value1.value).doubleValue() + * ((Double)value2.value).doubleValue()); + break; + case opc_ddiv: + newValue = new Double + (((Double)value1.value).doubleValue() + / ((Double)value2.value).doubleValue()); + break; + case opc_drem: + newValue = new Double + (((Double)value1.value).doubleValue() + % ((Double)value2.value).doubleValue()); + break; + default: + throw new jode.AssertError("Can't happen."); + } + shortInfo.flags |= CONSTANT; + shortInfo.constant = newValue; + result = new ConstantAnalyzerValue(newValue); + result.addConstantListener(shortInfo); + value1.addConstantListener(result); + value2.addConstantListener(result); + } else + result = unknownValue[size-1]; + mergeInfo(instr.nextByAddr, info.poppush(2*size, result)); + break; + } + case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: { + int size = 1 + (opcode - opc_ineg & 1); + ConstantAnalyzerValue value = info.getStack(size); + if (value.value != ConstantAnalyzerValue.VOLATILE) { + Object newValue; + switch (opcode) { + case opc_ineg: + newValue = new Integer + (-((Integer)value.value).intValue()); + break; + case opc_lneg: + newValue = new Long + (- ((Long)value.value).longValue()); + break; + case opc_fneg: + newValue = new Float + (- ((Float)value.value).floatValue()); + break; + case opc_dneg: + newValue = new Double + (- ((Double)value.value).doubleValue()); + break; + default: + throw new jode.AssertError("Can't happen."); + } + shortInfo.flags |= CONSTANT; + shortInfo.constant = newValue; + result = new ConstantAnalyzerValue(newValue); + result.addConstantListener(shortInfo); + value.addConstantListener(result); + } else + result = unknownValue[size-1]; + mergeInfo(instr.nextByAddr, info.poppush(size, result)); + break; + } + case opc_ishl: case opc_lshl: + case opc_ishr: case opc_lshr: + case opc_iushr: case opc_lushr: { + int size = 1 + (opcode - opc_iadd & 1); + ConstantAnalyzerValue value1 = info.getStack(size+1); + ConstantAnalyzerValue value2 = info.getStack(1); + if (value1.value != ConstantAnalyzerValue.VOLATILE + && value2.value != ConstantAnalyzerValue.VOLATILE) { + Object newValue; + switch (opcode) { + case opc_ishl: + newValue = new Integer + (((Integer)value1.value).intValue() + << ((Integer)value2.value).intValue()); + break; + case opc_ishr: + newValue = new Integer + (((Integer)value1.value).intValue() + >> ((Integer)value2.value).intValue()); + break; + case opc_iushr: + newValue = new Integer + (((Integer)value1.value).intValue() + >>> ((Integer)value2.value).intValue()); + break; + + case opc_lshl: + newValue = new Long + (((Long)value1.value).longValue() + << ((Integer)value2.value).intValue()); + break; + case opc_lshr: + newValue = new Long + (((Long)value1.value).longValue() + >> ((Integer)value2.value).intValue()); + break; + case opc_lushr: + newValue = new Long + (((Long)value1.value).longValue() + >>> ((Integer)value2.value).intValue()); + break; + default: + throw new jode.AssertError("Can't happen."); + } + shortInfo.flags |= CONSTANT; + shortInfo.constant = newValue; + result = new ConstantAnalyzerValue(newValue); + result.addConstantListener(shortInfo); + value1.addConstantListener(result); + value2.addConstantListener(result); + } else + result = unknownValue[size-1]; + mergeInfo(instr.nextByAddr, info.poppush(size+1, result)); + break; + } + case opc_iinc: { + ConstantAnalyzerValue local = info.getLocal(instr.localSlot); + if (local.value != ConstantAnalyzerValue.VOLATILE) { + result = new ConstantAnalyzerValue + (new Integer(((Integer)local.value).intValue() + + instr.intData)); + local.addConstantListener(result); + } else + result = unknownValue[0]; + mergeInfo(instr.nextByAddr, + info.setLocal(instr.localSlot, result)); + break; + } + case opc_i2l: case opc_i2f: case opc_i2d: + case opc_l2i: case opc_l2f: case opc_l2d: + case opc_f2i: case opc_f2l: case opc_f2d: + case opc_d2i: case opc_d2l: case opc_d2f: { + int insize = 1 + ((opcode - opc_i2l) / 3 & 1); + ConstantAnalyzerValue stack = info.getStack(insize); + if (stack.value != ConstantAnalyzerValue.VOLATILE) { + Object newVal; + switch(opcode) { + case opc_l2i: case opc_f2i: case opc_d2i: + newVal = new Integer(((Number)stack.value).intValue()); + break; + case opc_i2l: case opc_f2l: case opc_d2l: + newVal = new Long(((Number)stack.value).longValue()); + break; + case opc_i2f: case opc_l2f: case opc_d2f: + newVal = new Float(((Number)stack.value).floatValue()); + break; + case opc_i2d: case opc_l2d: case opc_f2d: + newVal = new Double(((Number)stack.value).doubleValue()); + break; + default: + throw new jode.AssertError("Can't happen."); + } + shortInfo.flags |= CONSTANT; + shortInfo.constant = newVal; + result = new ConstantAnalyzerValue(newVal); + result.addConstantListener(shortInfo); + stack.addConstantListener(result); + } else { + switch (opcode) { + case opc_i2l: case opc_f2l: case opc_d2l: + case opc_i2d: case opc_l2d: case opc_f2d: + result = unknownValue[1]; + break; + default: + result = unknownValue[0]; + } + } + mergeInfo(instr.nextByAddr, info.poppush(insize, result)); + break; + } + case opc_i2b: case opc_i2c: case opc_i2s: { + ConstantAnalyzerValue stack = info.getStack(1); + if (stack.value != ConstantAnalyzerValue.VOLATILE) { + int val = ((Integer)stack.value).intValue(); + switch(opcode) { + case opc_i2b: + val = (byte) val; + break; + case opc_i2c: + val = (char) val; + break; + case opc_i2s: + val = (short) val; + break; + } + shortInfo.flags |= CONSTANT; + shortInfo.constant = new Integer(val); + result = new ConstantAnalyzerValue(shortInfo.constant); + stack.addConstantListener(shortInfo); + stack.addConstantListener(result); + } else + result = unknownValue[0]; + mergeInfo(instr.nextByAddr, + info.poppush(1, result)); + break; + } + case opc_lcmp: { + ConstantAnalyzerValue val1 = info.getStack(4); + ConstantAnalyzerValue val2 = info.getStack(2); + if (val1.value != ConstantAnalyzerValue.VOLATILE + && val2.value != ConstantAnalyzerValue.VOLATILE) { + long value1 = ((Long) val1.value).longValue(); + long value2 = ((Long) val1.value).longValue(); + shortInfo.flags |= CONSTANT; + shortInfo.constant = new Integer(value1 == value2 ? 0 + : value1 < value2 ? -1 : 1); + result = new ConstantAnalyzerValue(shortInfo.constant); + result.addConstantListener(shortInfo); + val1.addConstantListener(result); + val2.addConstantListener(result); + } else + result = unknownValue[0]; + mergeInfo(instr.nextByAddr, info.poppush(4, result)); + break; + } + case opc_fcmpl: case opc_fcmpg: { + ConstantAnalyzerValue val1 = info.getStack(2); + ConstantAnalyzerValue val2 = info.getStack(1); + if (val1.value != ConstantAnalyzerValue.VOLATILE + && val2.value != ConstantAnalyzerValue.VOLATILE) { + float value1 = ((Float) val1.value).floatValue(); + float value2 = ((Float) val1.value).floatValue(); + shortInfo.flags |= CONSTANT; + shortInfo.constant = new Integer + (value1 == value2 ? 0 + : ( opcode == opc_fcmpg + ? (value1 < value2 ? -1 : 1) + : (value1 > value2 ? 1 : -1))); + result = new ConstantAnalyzerValue(shortInfo.constant); + result.addConstantListener(shortInfo); + val1.addConstantListener(result); + val2.addConstantListener(result); + } else + result = unknownValue[0]; + mergeInfo(instr.nextByAddr, info.poppush(2, result)); + break; + } + case opc_dcmpl: case opc_dcmpg: { + ConstantAnalyzerValue val1 = info.getStack(4); + ConstantAnalyzerValue val2 = info.getStack(2); + if (val1.value != ConstantAnalyzerValue.VOLATILE + && val2.value != ConstantAnalyzerValue.VOLATILE) { + double value1 = ((Double) val1.value).doubleValue(); + double value2 = ((Double) val1.value).doubleValue(); + shortInfo.flags |= CONSTANT; + shortInfo.constant = new Integer + (value1 == value2 ? 0 + : ( opcode == opc_dcmpg + ? (value1 < value2 ? -1 : 1) + : (value1 > value2 ? 1 : -1))); + result = new ConstantAnalyzerValue(shortInfo.constant); + result.addConstantListener(shortInfo); + val1.addConstantListener(result); + val2.addConstantListener(result); + } else + result = unknownValue[0]; + mergeInfo(instr.nextByAddr, info.poppush(4, result)); + break; + } + case opc_ifeq: case opc_ifne: + case opc_iflt: case opc_ifge: + case opc_ifgt: case opc_ifle: + case opc_if_icmpeq: case opc_if_icmpne: + case opc_if_icmplt: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: { + int size = 1; + ConstantAnalyzerValue stacktop = info.getStack(1); + ConstantAnalyzerValue other = null; + boolean known = stacktop.value != ConstantAnalyzerValue.VOLATILE; + if (opcode >= opc_if_icmpeq && opcode <= opc_if_acmpne) { + other = info.getStack(2); + size = 2; + known &= other.value != ConstantAnalyzerValue.VOLATILE; + } + if (known) { + stacktop.addConstantListener(info); + if (other != null) + other.addConstantListener(info); + + Instruction pc = instr.nextByAddr; + int value = -1; + if (opcode >= opc_if_acmpeq) { + if (opcode >= opc_ifnull) { + value = stacktop.value == null ? 0 : 1; + opcode += opc_ifeq - opc_ifnull; + } else { + value = stacktop.value == other.value ? 0 : 1; + } + } else { + value = ((Integer) stacktop.value).intValue(); + if (opcode >= opc_if_icmpeq) { + int val1 = ((Integer) other.value).intValue(); + value = (val1 == value ? 0 + : val1 < value ? -1 : 1); + opcode += opc_ifeq - opc_if_icmpeq; + } + } + if (value > 0 && (opcode == opc_ifgt || opcode == opc_ifge) + || value < 0 && (opcode == opc_iflt || opcode == opc_ifle) + || value == 0 && (opcode == opc_ifge || opcode == opc_ifle + || opcode == opc_ifeq)) + pc = instr.succs[0]; + + shortInfo.flags |= CONSTANTFLOW; + shortInfo.constant = pc; + mergeInfo(pc, info.pop(size)); + } else { + mergeInfo(instr.nextByAddr, info.pop(size)); + mergeInfo(instr.succs[0], info.pop(size)); + } + break; + } + case opc_goto: + mergeInfo(instr.succs[0], info.pop(0)); + break; + case opc_jsr: + mergeInfo(instr.nextByAddr, info.pop(0)); + mergeInfo(instr.succs[0], info.poppush(0, unknownValue[0])); + break; + case opc_tableswitch: { + ConstantAnalyzerValue stacktop = info.getStack(1); + if (stacktop.value != ConstantAnalyzerValue.VOLATILE) { + stacktop.addConstantListener(info); + Instruction pc; + int value = ((Integer) stacktop.value).intValue(); + int low = instr.intData; + if (value >= low && value <= low + instr.succs.length - 2) + pc = instr.succs[value - low]; + else + pc = instr.succs[instr.succs.length-1]; + shortInfo.flags |= CONSTANTFLOW; + shortInfo.constant = pc; + mergeInfo(pc, info.pop(1)); + } else { + for (int i=0; i < instr.succs.length; i++) + mergeInfo(instr.succs[i], info.pop(1)); + } + break; + } + case opc_lookupswitch: { + ConstantAnalyzerValue stacktop = info.getStack(1); + if (stacktop.value != ConstantAnalyzerValue.VOLATILE) { + stacktop.addConstantListener(info); + Instruction pc; + int value = ((Integer) stacktop.value).intValue(); + int[] values = (int[]) instr.objData; + pc = instr.succs[values.length]; + for (int i=0; i< values.length; i++) { + if (values[i] == value) { + pc = instr.succs[i]; + break; + } + } + shortInfo.flags |= CONSTANTFLOW; + shortInfo.constant = pc; + mergeInfo(pc, info.pop(1)); + } else { + for (int i=0; i < instr.succs.length; i++) + mergeInfo(instr.succs[i], info.pop(1)); + } + break; + } + case opc_ret: + case opc_ireturn: case opc_lreturn: + case opc_freturn: case opc_dreturn: case opc_areturn: + case opc_return: + case opc_athrow: + break; + + case opc_putstatic: + case opc_putfield: { + int size = (opcode == opc_putstatic) ? 0 : 1; + Reference ref = (Reference) instr.objData; + ClassIdentifier ci = (ClassIdentifier) + m.clazz.bundle.getIdentifier(ref.getClazz()); + if (ci != null) { + FieldIdentifier fi = (FieldIdentifier) + ci.getIdentifier(ref.getName(), ref.getType()); + fi.setNotConstant(); + } + Type type = Type.tType(ref.getType()); + mergeInfo(instr.nextByAddr, info.pop(size + type.stackSize())); + break; + } + case opc_getstatic: + case opc_getfield: { + int size = (opcode == opc_getstatic) ? 0 : 1; + Reference ref = (Reference) instr.objData; + Type type = Type.tType(ref.getType()); + ClassIdentifier ci = (ClassIdentifier) + m.clazz.bundle.getIdentifier(ref.getClazz()); + if (ci != null) { + FieldIdentifier fi = (FieldIdentifier) + ci.getIdentifier(ref.getName(), ref.getType()); + if (!fi.isNotConstant()) { + Object obj = fi.getConstant(); + if (obj == null) + obj = type.getDefaultValue(); + shortInfo.flags |= CONSTANT; + shortInfo.constant = obj; + result = new ConstantAnalyzerValue(obj); + result.addConstantListener(shortInfo); + fi.addFieldListener(m); + } else { + fi.setReachable(); + result = unknownValue[type.stackSize()-1]; + } + } else + result = unknownValue[type.stackSize()-1]; + mergeInfo(instr.nextByAddr, info.poppush(size, result)); + break; + } + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_invokevirtual: { + Reference ref = (Reference) instr.objData; + handleReference(ref, opcode == opc_invokevirtual + || opcode == opc_invokeinterface); + MethodType mt = (MethodType) Type.tType(ref.getType()); + int size = (opcode == opc_invokestatic) ? 0 : 1; + for (int i=mt.getParameterTypes().length-1; i >=0; i--) + size += mt.getParameterTypes()[i].stackSize(); + ConstantAnalyzerInfo newInfo; + if (mt.getReturnType() != Type.tVoid) { + ConstantAnalyzerValue returnVal = + unknownValue[mt.getReturnType().stackSize()-1]; + newInfo = info.poppush(size, returnVal); + } else + newInfo = info.pop(size); + mergeInfo(instr.nextByAddr, newInfo); + break; + } + + case opc_new: { + handleClass((String) instr.objData); + mergeInfo(instr.nextByAddr, info.poppush(0, unknownValue[0])); + break; + } + case opc_newarray: { + mergeInfo(instr.nextByAddr, info.poppush(1, unknownValue[0])); + break; + } + case opc_anewarray: { + handleClass((String) instr.objData); + mergeInfo(instr.nextByAddr, info.poppush(1, unknownValue[0])); + break; + } + case opc_arraylength: { + ConstantAnalyzerValue array = info.getStack(1); + if (array.value != ConstantAnalyzerValue.VOLATILE + && array.value != null) { + shortInfo.flags |= CONSTANT; + shortInfo.constant = new Integer(Array.getLength(array.value)); + result = new ConstantAnalyzerValue(shortInfo.constant); + result.addConstantListener(shortInfo); + array.addConstantListener(result); + } else + result = unknownValue[0]; + mergeInfo(instr.nextByAddr, info.poppush(1, result)); + break; + } + case opc_checkcast: { + handleClass((String) instr.objData); + mergeInfo(instr.nextByAddr, info.pop(0)); + break; + } + case opc_instanceof: { + handleClass((String) instr.objData); + mergeInfo(instr.nextByAddr, info.poppush(1, unknownValue[0])); + break; + } + case opc_monitorenter: + case opc_monitorexit: + mergeInfo(instr.nextByAddr, info.pop(1)); + break; + case opc_multianewarray: + handleClass((String) instr.objData); + mergeInfo(instr.nextByAddr, + info.poppush(instr.intData, unknownValue[0])); + break; + default: + throw new jode.AssertError("Invalid opcode "+opcode); + } + + } + + public void analyzeCode() { + working = true; + if (constInfos == null) + constInfos = new Hashtable(); + instrStack = new Stack(); + bytecode.getFirstInstr().tmpInfo = new ConstantAnalyzerInfo + (bytecode.getMaxLocals(), m.info.isStatic(), m.info.getType()); + instrStack.push(bytecode.getFirstInstr()); + try { + while (!instrStack.isEmpty()) { + Instruction instr = (Instruction) instrStack.pop(); +// System.err.println("addr: "+m+","+instr.addr+";"+instr.tmpInfo); + handleOpcode(instr); + } + } catch (OutOfMemoryError err) { + System.err.println("Out of memory"); + System.exit(0); + } + + Handler[] handlers = bytecode.getExceptionHandlers(); + for (int i=0; i< handlers.length; i++) { + if (handlers[i].catcher.tmpInfo != null + && handlers[i].type != null) + m.clazz.bundle.reachableIdentifier(handlers[i].type, false); + } + working = false; + for (Instruction instr = bytecode.getFirstInstr(); + instr != null; instr = instr.nextByAddr) + instr.tmpInfo = null; + instrStack = null; +// System.gc(); + } + + public void fieldNotConstant(IdentifierEvent ev) { + if (!working) { + m.clazz.bundle.analyzeIdentifier(m); + return; + } + FieldIdentifier fi = (FieldIdentifier) ev.getSource(); + for (Instruction instr = bytecode.getFirstInstr(); + instr != null; instr = instr.nextByAddr) { + if (instr.opcode == opc_getfield + || instr.opcode == opc_getstatic) { + Reference ref = (Reference) instr.objData; + if (ref.getName().equals(fi.getName()) + && ref.getType().equals(fi.getType()) + && instr.tmpInfo != null) { + if (!instrStack.contains(instr)) + instrStack.push(instr); + } + } + } + } + + + public void insertOnePop(Instruction instr, int count) { + /* Add a goto instruction after this opcode. */ + Instruction pop = instr.insertInstruction(); + pop.length = 1; + pop.opcode = Instruction.opc_pop - 1 + count; + } + + public void insertPop(Instruction instr) { + switch(instr.opcode) { + case opc_ldc: + case opc_ldc2_w: + case opc_iload: case opc_lload: + case opc_fload: case opc_dload: case opc_aload: + case opc_getstatic: + break; + case opc_arraylength: + case opc_getfield: + case opc_i2l: case opc_i2f: case opc_i2d: + case opc_f2i: case opc_f2l: case opc_f2d: + case opc_i2b: case opc_i2c: case opc_i2s: + case opc_ineg: case opc_fneg: + insertOnePop(instr, 1); + break; + case opc_lcmp: + case opc_dcmpg: case opc_dcmpl: + case opc_ladd: case opc_dadd: + case opc_lsub: case opc_dsub: + case opc_lmul: case opc_dmul: + case opc_ldiv: case opc_ddiv: + case opc_lrem: case opc_drem: + case opc_land: case opc_lor : case opc_lxor: + insertOnePop(instr, 2); + /* fall through */ + case opc_fcmpg: case opc_fcmpl: + case opc_l2i: case opc_l2f: case opc_l2d: + case opc_d2i: case opc_d2l: case opc_d2f: + case opc_lneg: case opc_dneg: + case opc_iadd: case opc_fadd: + case opc_isub: case opc_fsub: + case opc_imul: case opc_fmul: + case opc_idiv: case opc_fdiv: + case opc_irem: case opc_frem: + case opc_iand: case opc_ior : case opc_ixor: + case opc_ishl: case opc_ishr: case opc_iushr: + case opc_iaload: case opc_laload: + case opc_faload: case opc_daload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: + insertOnePop(instr, 2); + break; + + case opc_lshl: case opc_lshr: case opc_lushr: + insertOnePop(instr, 1); + insertOnePop(instr, 2); + break; + case opc_putstatic: + case opc_putfield: + if (Type.tType(((Reference)instr.objData).getType()) + .stackSize() == 2) { + insertOnePop(instr, 2); + if (instr.opcode == opc_putfield) + insertOnePop(instr, 1); + } else + insertOnePop(instr, (instr.opcode == opc_putfield) ? 2 : 1); + } + } + + public void appendJump(Instruction instr, Instruction dest) { + /* Add a goto instruction after this opcode. */ + Instruction second = instr.appendInstruction(true); + second.opcode = Instruction.opc_goto; + second.length = 3; + second.succs = new Instruction[] { dest }; + dest.preds.addElement(second); + } + + public BytecodeInfo stripCode() { + if (constInfos == null) + analyzeCode(); + for (Instruction instr = bytecode.getFirstInstr(); + instr != null; instr = instr.nextByAddr) { + ConstantInfo info = (ConstantInfo) constInfos.get(instr); + if (info == null || (info.flags & REACHABLE) == 0) { + /* This instruction can't be reached logically */ + instr.removeInstruction(); + } else if ((info.flags & CONSTANT) != 0) { + insertPop(instr); + if (instr.opcode > opc_ldc2_w) { + instr.opcode = (info.constant instanceof Long + || info.constant instanceof Double + ? opc_ldc2_w : opc_ldc); + instr.localSlot = -1; + instr.length = 2; + instr.objData = info.constant; +// System.err.println(m+": Replacing " +// +opcodeString[instr.opcode] +// +" with constant "+info.constant); + } + instr.tmpInfo = null; + } else if ((info.flags & CONSTANTFLOW) != 0) { + Instruction pc = (Instruction) info.constant; + if (instr.opcode >= opc_if_icmpeq + && instr.opcode <= opc_if_acmpne) + instr.opcode = opc_pop2; + else + instr.opcode = opc_pop; + instr.alwaysJumps = false; + instr.succs[0].preds.removeElement(instr); + instr.succs = null; + instr.length = 1; + while (instr.nextByAddr != null) { + ConstantInfo nextinfo + = (ConstantInfo) constInfos.get(instr.nextByAddr); + if (nextinfo != null && (nextinfo.flags & REACHABLE) != 0) + break; + /* Next instruction can't be reached logically */ + instr.nextByAddr.removeInstruction(); + } + + if (pc != instr.nextByAddr) { + appendJump(instr, pc); + instr = instr.nextByAddr; + } + + } else { + int opcode = instr.opcode; + switch (opcode) { + case opc_nop: + instr.removeInstruction(); + break; + + case opc_goto: + while (instr.nextByAddr != null) { + ConstantInfo nextinfo + = (ConstantInfo) constInfos.get(instr.nextByAddr); + if (nextinfo != null + && (nextinfo.flags & REACHABLE) != 0) + break; + /* Next instruction can't be reached logically */ + instr.nextByAddr.removeInstruction(); + } + /* fall through */ + case opc_ifeq: case opc_ifne: + case opc_iflt: case opc_ifge: + case opc_ifgt: case opc_ifle: + case opc_if_icmpeq: case opc_if_icmpne: + case opc_if_icmplt: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: + if (instr.succs[0] == instr.nextByAddr) + instr.removeInstruction(); + break; + + case opc_putstatic: + case opc_putfield: { + Reference ref = (Reference) instr.objData; + ClassIdentifier ci = (ClassIdentifier) + m.clazz.bundle.getIdentifier(ref.getClazz()); + if (ci != null) { + FieldIdentifier fi = (FieldIdentifier) + ci.getIdentifier(ref.getName(), ref.getType()); + if (jode.Obfuscator.shouldStrip && !fi.isReachable()) { + insertPop(instr); + instr.removeInstruction(); + } + } + break; + } + } + instr.tmpInfo = null; + } + } + return bytecode; + } +} diff --git a/jode/jode/obfuscator/FieldListener.java b/jode/jode/obfuscator/FieldListener.java new file mode 100644 index 0000000..9f7a7cc --- /dev/null +++ b/jode/jode/obfuscator/FieldListener.java @@ -0,0 +1,35 @@ +/* FieldListener 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; + +/** + * This is the interface for field listeners, to get information when + * something interesting happens to a field. + * + * @author Jochen Hoenicke + */ + +public interface FieldListener extends java.util.EventListener { + + /** + * This method gets called, when a field can no longer be constant. + */ + public void fieldNotConstant(IdentifierEvent ev); +} diff --git a/jode/jode/obfuscator/IdentifierEvent.java b/jode/jode/obfuscator/IdentifierEvent.java new file mode 100644 index 0000000..c45fbe1 --- /dev/null +++ b/jode/jode/obfuscator/IdentifierEvent.java @@ -0,0 +1,47 @@ +/* IdentifierEvent 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; + +/** + * + * @author Jochen Hoenicke + */ +public class IdentifierEvent extends java.util.EventObject { + /* 0 - 9: general events */ + public final static int REACHABLE = 0; + public final static int PRESERVED = 1; + + /* 10 - 19: field events */ + public final static int CONSTANT = 10; + /* 20 - 29: method events */ + /* 30 - 39: class events */ + /* 40 - 49: package events */ + + public final int id; + + public IdentifierEvent(Identifier source, int id) { + super(source); + this.id = id; + } + + public final int getID() { + return id; + } +} diff --git a/jode/jode/obfuscator/IdentifierListener.java b/jode/jode/obfuscator/IdentifierListener.java new file mode 100644 index 0000000..eefaaa4 --- /dev/null +++ b/jode/jode/obfuscator/IdentifierListener.java @@ -0,0 +1,34 @@ +/* IdentifierListener 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; + +/** + * This is the interface for identifier listeners, to get information when + * something interesting happens to a identifier. + * + * @author Jochen Hoenicke + */ + +public interface IdentifierListener extends java.util.EventListener { + /** + * This method gets called, when a identifier is reachable. + */ + public void identifierReachable(IdentifierEvent ev); +} diff --git a/jode/jode/obfuscator/LogicalDeadCodeOptimizer.java b/jode/jode/obfuscator/LogicalDeadCodeOptimizer.java new file mode 100644 index 0000000..d929a71 --- /dev/null +++ b/jode/jode/obfuscator/LogicalDeadCodeOptimizer.java @@ -0,0 +1,224 @@ +/* LogicalDeadCodeOptimizer 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; + +/** + * This class analyzes the method to detect and remove logical dead + * code. If a field, local or stack entry, is tested to have a + * certain value, that is remembered and if a similar test occurs + * again, we may know, that it always evaluates to true + * resp. false.
+ * + * Each field/local/stack entry has a Constraint, that tells e.g. if + * that value is always a constant, always not zero, always zero, or + * always equal to another stack entry etc. These constraints can be + * propagated, and if the condition of a opc_if is always the same, + * that opc_if can be removed resp. replaces by a opc_goto.
+ * + * @author Jochen Hoenicke + */ +public class LogicalDeadCodeOptimizer { + class Constraint { + /* The constant are aligned so that the operation ^1 negates the + * comparison and like the VM opcodes. + */ + public int EQ = 0; + public int NE = 1; + public int LT = 2; + public int GE = 3; + public int GT = 4; + public int LE = 5; + + /** + * This gives the compareOps for this constraint (one of + * EQ/NE/GE/GT/LE/LT). */ + int compareOp; + /** + * The reference value to which is compared (this is the + * second argument of the compare). + */ + ConstrainedValue reference; + + } + + class ConstrainedValue { + boolean isConstant; + Object constant; + + /** + * If this is not constant, this are all constraints. + */ + Constraint[] constraints; + + + + public boolean implies(Constraint constraint) { + if (isConstant) { + // check if always + // value.constant compareOp reference + return constraint.reference.compareToConstant + (value.constant, compareOp) == 1; + } else { + // + } + } + + /** + * Compares this object with other. + * @param compareOp one of Constraint.EQ/NE/LT/LE/GT/GE. + * @param other a constant, null, a String or a Number. + * @return 1, if (other compareOp this) always true,
+ * 0, if (other compareOp this) always false,
+ * -1, otherwise + */ + public int compareToConstant(Object other, int compareOp) { + if (isConstant) { + switch (compareOp) { + case EQ: + return other.equals(constant) ? 1 : 0; + case NE: + return other.equals(constant) ? 0 : 1; + case LE: + case LT: + case GE: + case GT: { + /* This must be a number */ + int cmp = ((Number)other).compareTo((Number)constant); + if (compareOp == LE && cmp <= 0 + || compareOp == LT && cmp < 0 + || compareOp == GE && cmp >= 0 + || compareOp == GT && cmp > 0) + return 1; + return 0; + } + } + } else { + /* Not a constant, try the constraints. */ + + /* First we find all equal references */ + Vector equalRefs = new Vector(); + equalRefs.add(this); + for (int i=0; i < equalRefs.size(); i++) { + Constraint[] cs = ((ConstrainedValue) + equalRefs.elementAt(i)).constraints; + for (int j=0; j < cs.count; j++) { + if (cs[j].compareOp == EQ + && !equalRefs.contains(cs[j].reference)) + equalRefs.addElement(cs[j].reference); + } + } + /* If we wanted to only check for EQ or NE we can do this now. + */ + if (compareOp == EQ || compareOp == NE) { + for (int i=0; i < equalRefs.size(); i++) { + Constraint[] cs = ((ConstrainedValue) + equalRefs.elementAt(i)).constraints; + for (int j=0; j < cs.count; j++) { + if ((1 << cs[j].compareOp + & (1<= NE + /* consider same compares */ + && cs.compareOp == cmpop + /* and consider same compares except equal */ + && cs.compareOp ^ 7 == cmpop)) { + + /* if cs.compareOp is greater or lower + * (not equal), we can allow equal in cmpop */ + if (cs.compareOp != EQ + && (cs.compareOp & 1) == 0 + && (cmpop & 1) == 0) + cmpop ^= 7; + if (cs.reference.isConstant()) { + if (other.compareToConstant + (cmpop, cs.reference.isConstant()) == 1) + return 1; + } else if (!stack.contains(cs.reference)) { + stack.push(cs.reference); + cmpopStack.push(new Integer(cmpop)); + } + } + } + } + return -1; + } + } + } + + /** + * OPEN QUESTIONS: + * we have a variable whose value is > 0. + * is after an increase the value still >0? + * (not necessarily, e.g. value == Integer.MAX_VALUE) + */ + + /* Every local/stack has a list of Constraint. + * Operations: + * -when two control flows flow together, we need to intersect + * Constraints: All constraints are considered, a constraint + * is taken, if it is implied by a constraint in the other list. + * + * -load operations copy local Constraint to stack Constraint. + * -store operations copy stack Constraint to local Constraint. + */ +} + + + diff --git a/jode/jode/obfuscator/RemovePopAnalyzer.java b/jode/jode/obfuscator/RemovePopAnalyzer.java new file mode 100644 index 0000000..5571602 --- /dev/null +++ b/jode/jode/obfuscator/RemovePopAnalyzer.java @@ -0,0 +1,424 @@ +/* RemovePopAnalyzer 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 jode.bytecode.*; +import jode.AssertError; +import jode.MethodType; +import jode.Type; + +public class RemovePopAnalyzer implements CodeAnalyzer, Opcodes { + MethodIdentifier m; + BytecodeInfo bytecode; + + public RemovePopAnalyzer(BytecodeInfo bytecode, MethodIdentifier m) { + this.m = m; + this.bytecode = bytecode; + } + + /** + * Reads the opcodes out of the code info and determine its + * references + * @return an enumeration of the references. + */ + public void analyzeCode() { + } + + class PopInfo { + int firstPop = 0; + int[] pops; + Instruction nextInstr; + } + + Instruction findMatchingPush(Instruction instr) { + int count = 0; + while (true) { + if (instr.preds.size() != 1) + return null; + instr = instr.prevByAddr; + switch (instr.opcode) { + case opc_ldc2_w: + case opc_lload: case opc_dload: + if (count < 2) + return count == 0 ? instr : null; + count -= 2; + break; + case opc_ldc: + case opc_iload: case opc_fload: case opc_aload: + case opc_new: + if (count == 0) + return instr; + count --; + break; + + case opc_iaload: case opc_faload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: + if (count == 0) + return instr; + count++; + break; + + case opc_dup: case opc_dup_x1: case opc_dup_x2: { + /* XXX This is a very special case, if we pop a value + * that is dupped we can omit the dup; it doesn't matter + * if we pop the dupped value or the original value. + */ + int depth = (instr.opcode - opc_dup); + if (count < 2 + depth) + return (count == 0 || count == depth+1) ? instr : null; + count --; + break; + } + case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: { + int depth = (instr.opcode - opc_dup2); + if (count < 4 + depth) + return count == 0 ? instr : null; + count -= 2; + break; + } + case opc_swap: + case opc_lneg: case opc_dneg: + case opc_l2d: case opc_d2l: + case opc_laload: case opc_daload: + if (count < 2) + return count == 0 ? instr : null; + break; + case opc_ineg: case opc_fneg: + case opc_i2f: case opc_f2i: + case opc_i2b: case opc_i2c: case opc_i2s: + case opc_newarray: case opc_anewarray: + case opc_arraylength: + case opc_checkcast: + case opc_instanceof: + if (count == 0) + return instr; + break; + case opc_iadd: case opc_fadd: + case opc_isub: case opc_fsub: + case opc_imul: case opc_fmul: + case opc_idiv: case opc_fdiv: + case opc_irem: case opc_frem: + case opc_iand: case opc_ior : case opc_ixor: + case opc_ishl: case opc_ishr: case opc_iushr: + case opc_fcmpl: case opc_fcmpg: + case opc_l2i: case opc_l2f: + case opc_d2i: case opc_d2f: + if (count == 0) + return instr; + count++; + break; + case opc_ladd: case opc_dadd: + case opc_lsub: case opc_dsub: + case opc_lmul: case opc_dmul: + case opc_ldiv: case opc_ddiv: + case opc_lrem: case opc_drem: + case opc_land: case opc_lor : case opc_lxor: + if (count < 2) + return count == 0 ? instr : null; + count += 2; + break; + case opc_lshl: case opc_lshr: case opc_lushr: + if (count < 2) + return count == 0 ? instr : null; + count++; + break; + case opc_i2l: case opc_i2d: + case opc_f2l: case opc_f2d: + if (count < 2) + return count == 0 ? instr : null; + count--; + break; + + case opc_lcmp: + case opc_dcmpl: case opc_dcmpg: + if (count == 0) + return instr; + count += 3; + break; + + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: { + Reference ref = (Reference) instr.objData; + MethodType mt = (MethodType) Type.tType(ref.getType()); + if (count < mt.getReturnType().stackSize()) + return (count == 0) ? instr : null; + count -= mt.getReturnType().stackSize(); + if (instr.opcode != opc_invokestatic) + count++; + for (int i = mt.getParameterTypes().length-1; i >= 0; i--) + count += mt.getParameterTypes()[i].stackSize(); + break; + } + + case opc_getstatic: + case opc_getfield: { + Reference ref = (Reference) instr.objData; + int size = Type.tType(ref.getType()).stackSize(); + if (count < size) + return count == 0 ? instr : null; + count -= size; + if (instr.opcode == opc_getfield) + count++; + break; + } + + case opc_multianewarray: { + if (count == 0) + return instr; + int dims = instr.prevByAddr.intData; + count += dims - 1; + break; + } + + case opc_nop: + case opc_iinc: + break; + case opc_putfield: + count++; + /* fall through */ + case opc_putstatic: + count += instr.objData instanceof Long + || instr.objData instanceof Double ? 2 : 1; + break; + case opc_monitorenter: + case opc_monitorexit: + case opc_istore: + case opc_fstore: case opc_astore: + case opc_pop: + count ++; + break; + + case opc_lstore: case opc_dstore: + case opc_pop2: + count += 2; + break; + + case opc_iastore: + case opc_fastore: case opc_aastore: + case opc_bastore: case opc_castore: case opc_sastore: + count += 3; + break; + case opc_lastore: case opc_dastore: + count += 4; + break; + default: + return null; + } + } + } + + static Instruction shrinkPop(Instruction popInstr, int amount) { + int newPop = popInstr.opcode - (opc_pop-1) - amount; + if (newPop < 0) + throw new jode.AssertError("pop1 on long or double"); + if (newPop == 0) { + Instruction nextInstr = popInstr.nextByAddr; + popInstr.removeInstruction(); + return nextInstr; + } + popInstr.opcode = opc_pop - 1 + newPop; + return popInstr; + } + + public BytecodeInfo stripCode() { + Instruction instr = bytecode.getFirstInstr(); + while (instr != null) { + switch (instr.opcode) { + case opc_nop: { + Instruction nextInstr = instr.nextByAddr; + instr.removeInstruction(); + instr = nextInstr; + continue; + } + case opc_pop: + case opc_pop2: { + Instruction prevInstr = findMatchingPush(instr); + int opcode = prevInstr == null ? -1 : prevInstr.opcode; + switch (opcode) { + case opc_ldc2_w: + case opc_lload: case opc_dload: + prevInstr.removeInstruction(); + instr = shrinkPop(instr, 2); + continue; + case opc_ldc: + case opc_iload: case opc_fload: case opc_aload: + case opc_dup: + case opc_new: + prevInstr.removeInstruction(); + instr = shrinkPop(instr, 1); + continue; + case opc_iaload: case opc_faload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: + /* We have to pop one entry more. */ + prevInstr.opcode = opc_pop; + instr = prevInstr; + continue; + + case opc_dup_x1: + prevInstr.opcode = opc_swap; + instr = shrinkPop(instr, 1); + continue; + case opc_dup2: + if (instr.opcode == opc_pop2) { + prevInstr.removeInstruction(); + } else + prevInstr.opcode = opc_dup; + instr = instr.nextByAddr; + instr.prevByAddr.removeInstruction(); + continue; + + case opc_lneg: case opc_dneg: + case opc_l2d: case opc_d2l: + case opc_laload: case opc_daload: + if (instr.opcode != opc_pop2) + break; + /* fall through */ + case opc_ineg: case opc_fneg: + case opc_i2f: case opc_f2i: + case opc_i2b: case opc_i2c: case opc_i2s: + case opc_newarray: case opc_anewarray: + case opc_arraylength: + case opc_instanceof: + prevInstr.removeInstruction(); + continue; + + case opc_iadd: case opc_fadd: + case opc_isub: case opc_fsub: + case opc_imul: case opc_fmul: + case opc_idiv: case opc_fdiv: + case opc_irem: case opc_frem: + case opc_iand: case opc_ior : case opc_ixor: + case opc_ishl: case opc_ishr: case opc_iushr: + case opc_fcmpl: case opc_fcmpg: + case opc_l2i: case opc_l2f: + case opc_d2i: case opc_d2f: + prevInstr.opcode = opc_pop2; + shrinkPop(instr, 1); + instr = prevInstr; + continue; + case opc_ladd: case opc_dadd: + case opc_lsub: case opc_dsub: + case opc_lmul: case opc_dmul: + case opc_ldiv: case opc_ddiv: + case opc_lrem: case opc_drem: + case opc_land: case opc_lor : case opc_lxor: + if (instr.opcode != opc_pop2) + break; + prevInstr.opcode = opc_pop2; + instr = prevInstr; + continue; + case opc_lshl: case opc_lshr: case opc_lushr: + if (instr.opcode != opc_pop2) + break; + prevInstr.opcode = opc_pop; + instr = prevInstr; + continue; + + case opc_i2l: case opc_i2d: + case opc_f2l: case opc_f2d: + if (instr.opcode != opc_pop2) + break; + prevInstr.removeInstruction(); + instr.opcode = opc_pop; + continue; + + case opc_lcmp: + case opc_dcmpl: case opc_dcmpg: + prevInstr.opcode = opc_pop2; + if (instr.opcode == opc_pop) + instr.opcode = opc_pop2; + else { + Instruction thirdPop = instr.appendInstruction(false); + thirdPop.length = 1; + thirdPop.opcode = opc_pop; + } + instr = prevInstr; + continue; + + case opc_getstatic: + case opc_getfield: { + Reference ref = (Reference) prevInstr.objData; + int count = Type.tType(ref.getType()).stackSize(); + if (prevInstr.opcode == opc_getfield) + count--; + prevInstr.removeInstruction(); + if (count > 0) + instr = shrinkPop(instr, count); + continue; + } + + case opc_multianewarray: { + int dims = prevInstr.intData; + prevInstr.removeInstruction(); + if (dims == 0) + instr = shrinkPop(instr, 1); + else { + dims--; + while (dims > 0) { + Instruction aPop = instr.insertInstruction(); + aPop.length = 1; + aPop.opcode = opc_pop; + dims--; + instr = aPop; + } + } + continue; + } + + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + if (((MethodType) + Type.tType(((Reference) prevInstr.objData).getType())) + .getReturnType().stackSize() != 1) + break; + /* fall through */ + case opc_checkcast: + case -1: + if (instr.opcode == opc_pop2) { + /* This is/may be a double pop on a single value + * split it and continue with second half + */ + instr.opcode = opc_pop; + instr = instr.appendInstruction(false); + instr.opcode = opc_pop; + instr.length = 1; + continue; + } + } + if (instr.opcode == opc_pop + && instr.prevByAddr.opcode == opc_pop) { + /* merge two single pops together. */ + instr.prevByAddr.removeInstruction(); + instr.opcode = opc_pop2; + } + /* Cant do anything with this pop */ + } + /* fall through */ + default: + instr = instr.nextByAddr; + continue; + } + } + return bytecode; + } +} diff --git a/jode/jode/obfuscator/SimpleAnalyzer.java b/jode/jode/obfuscator/SimpleAnalyzer.java new file mode 100644 index 0000000..a535ee9 --- /dev/null +++ b/jode/jode/obfuscator/SimpleAnalyzer.java @@ -0,0 +1,125 @@ +/* SimpleAnalyzer 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 jode.bytecode.*; +import jode.Type; + +public class SimpleAnalyzer implements CodeAnalyzer, Opcodes { + MethodIdentifier m; + BytecodeInfo bytecode; + + public SimpleAnalyzer(BytecodeInfo bytecode,MethodIdentifier m) { + this.m = m; + this.bytecode = bytecode; + } + + /** + * Reads the opcodes out of the code info and determine its + * references + * @return an enumeration of the references. + */ + public void analyzeCode() { + for (Instruction instr = bytecode.getFirstInstr(); + instr != null; instr = instr.nextByAddr) { + switch (instr.opcode) { + case opc_new: + case opc_anewarray: + case opc_checkcast: + case opc_instanceof: + case opc_multianewarray: { + String clName = (String) instr.objData; + if (clName.charAt(0) == '[') { + int i; + for (i=0; i< clName.length(); i++) + if (clName.charAt(i) != '[') + break; + if (i >= clName.length() || clName.charAt(i) != 'L') + break; + int index = clName.indexOf(';', i); + if (index != clName.length()-1) + break; + clName = clName.substring(i+1, index); + } + m.clazz.bundle.reachableIdentifier(clName, false); + break; + } + case opc_getstatic: + case opc_getfield: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_invokevirtual: { + Reference ref = (Reference) instr.objData; + m.clazz.bundle.reachableIdentifier + (ref.getClazz()+"."+ref.getName()+"."+ref.getType(), + instr.opcode == opc_invokevirtual + || instr.opcode == opc_invokeinterface); + break; + } + } + } + + Handler[] handlers = bytecode.getExceptionHandlers(); + for (int i=0; i< handlers.length; i++) { + if (handlers[i].type != null) + m.clazz.bundle.reachableIdentifier(handlers[i].type, false); + } + } + + public BytecodeInfo stripCode() { + for (Instruction instr = bytecode.getFirstInstr(); + instr != null; instr = instr.nextByAddr) { + if (instr.opcode == opc_putstatic + || instr.opcode == opc_putfield) { + Reference ref = (Reference) instr.objData; + ClassIdentifier ci = (ClassIdentifier) + m.clazz.bundle.getIdentifier(ref.getClazz()); + if (ci != null) { + FieldIdentifier fi = (FieldIdentifier) + ci.getIdentifier(ref.getName(), ref.getType()); + if (jode.Obfuscator.shouldStrip && !fi.isReachable()) { + /* Replace instruction with pop opcodes. */ + int stacksize = + (instr.opcode + == Instruction.opc_putstatic) ? 0 : 1; + stacksize += Type.tType(ref.getType()).stackSize(); + if (stacksize == 3) { + /* Add a pop instruction after this opcode. */ + Instruction second = new Instruction(bytecode); + second.addr = instr.addr+1; + second.length = 1; + second.opcode = Instruction.opc_pop; + second.nextByAddr = instr.nextByAddr; + instr.nextByAddr = second; + second.nextByAddr.preds.removeElement(instr); + second.nextByAddr.preds.addElement(second); + stacksize--; + } + instr.objData = null; + instr.intData = 0; + instr.opcode = Instruction.opc_pop - 1 + stacksize; + instr.length = 1; + } + } + } + } + return bytecode; + } +} diff --git a/jode/jode/obfuscator/WildCard.java b/jode/jode/obfuscator/WildCard.java new file mode 100644 index 0000000..f3637d6 --- /dev/null +++ b/jode/jode/obfuscator/WildCard.java @@ -0,0 +1,43 @@ +/* WildCard 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; + +public class WildCard { + + public static boolean matches(String wildcard, String test) { + int indexWild = wildcard.indexOf('*'); + if (indexWild == -1) + return wildcard.equals(test); + if (!test.startsWith(wildcard.substring(0, indexWild))) + return false; + + test = test.substring(indexWild); + int nextWild; + while ((nextWild = wildcard.indexOf('*', indexWild + 1)) != -1) { + String pattern = wildcard.substring(indexWild+1, nextWild); + while (!test.startsWith(pattern)) + test = test.substring(1); + test = test.substring(nextWild - indexWild); + indexWild = nextWild; + } + + return test.endsWith(wildcard.substring(indexWild+1)); + } +}