git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1174 379699f6-c40d-0410-875b-85095c16579ebranch_1_1
parent
f8384e9928
commit
49eb9190e3
@ -0,0 +1,2 @@ |
|||||||
|
Makefile |
||||||
|
Makefile.in |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,81 @@ |
|||||||
|
/* KeywordRenamer 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.modules; |
||||||
|
import jode.obfuscator.*; |
||||||
|
import @COLLECTIONS@.Collection; |
||||||
|
import @COLLECTIONS@.Iterator; |
||||||
|
import @COLLECTIONEXTRA@.UnsupportedOperationException; |
||||||
|
|
||||||
|
public class KeywordRenamer implements Renamer, OptionHandler { |
||||||
|
String keywords[]; |
||||||
|
Renamer backup; |
||||||
|
|
||||||
|
public KeywordRenamer() { |
||||||
|
keywords = new String[] { |
||||||
|
"if", "else", "for", "while", "throw", "return", |
||||||
|
"class", "interface", "implements", "extends", |
||||||
|
"instanceof", "new", |
||||||
|
"int", "boolean", "long", "float", "double", "short", |
||||||
|
"Object", "String", "Thread", |
||||||
|
"public", "protected", "private", |
||||||
|
"static", "synchronized", "strict", "transient", "abstract", |
||||||
|
"volatile", "final", |
||||||
|
/* Not really keywords, but very confusing anyway. */ |
||||||
|
"Object", "String", "Runnable", "StringBuffer", "Vector" |
||||||
|
}; |
||||||
|
backup = new StrongRenamer(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setOption(String option, Collection values) { |
||||||
|
if (option.startsWith("keywords")) { |
||||||
|
keywords = (String[]) values.toArray(new String[values.size()]); |
||||||
|
} else if (option.startsWith("backup")) { |
||||||
|
if (values.size() != 1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Only one backup is allowed"); |
||||||
|
backup = (Renamer) values.iterator().next(); |
||||||
|
} else |
||||||
|
throw new IllegalArgumentException("Invalid option `"+option+"'"); |
||||||
|
} |
||||||
|
|
||||||
|
public Iterator generateNames(final Identifier ident) { |
||||||
|
return new Iterator() { |
||||||
|
int pos = 0; |
||||||
|
Iterator backing = null; |
||||||
|
|
||||||
|
public boolean hasNext() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
public Object next() { |
||||||
|
if (pos < keywords.length) |
||||||
|
return keywords[pos++]; |
||||||
|
|
||||||
|
if (backing == null) |
||||||
|
backing = backup.generateNames(ident); |
||||||
|
|
||||||
|
return backing.next(); |
||||||
|
} |
||||||
|
|
||||||
|
public void remove() { |
||||||
|
throw new UnsupportedOperationException(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,941 @@ |
|||||||
|
/* 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.modules; |
||||||
|
import java.util.*; |
||||||
|
import jode.bytecode.*; |
||||||
|
import jode.obfuscator.*; |
||||||
|
import jode.AssertError; |
||||||
|
import jode.GlobalOptions; |
||||||
|
|
||||||
|
import @COLLECTIONS@.Iterator; |
||||||
|
import @COLLECTIONS@.ListIterator; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class takes some bytecode and tries to minimize the number |
||||||
|
* of locals used. It will also remove unnecessary stores. |
||||||
|
* |
||||||
|
* This class can only work on verified code. There should also be no |
||||||
|
* deadcode, since the verifier doesn't check that deadcode behaves |
||||||
|
* okay. |
||||||
|
* |
||||||
|
* This is done in two phases. First we determine which locals are |
||||||
|
* the same, and which locals have a overlapping life time. In the |
||||||
|
* second phase we will then redistribute the locals with a coloring |
||||||
|
* graph algorithm. |
||||||
|
* |
||||||
|
* The idea for the first phase is: For each read we follow the |
||||||
|
* instruction flow backward to find the corresponding writes. We can |
||||||
|
* also merge with another control flow that has a different read, in |
||||||
|
* this case we merge with that read, too. |
||||||
|
* |
||||||
|
* The tricky part is the subroutine handling. We follow the local |
||||||
|
* that is used in a ret and find the corresponding jsr target (there |
||||||
|
* must be only one, if the verifier should accept this class). While |
||||||
|
* we do this we remember in the info of the ret, which locals are |
||||||
|
* used in that subroutine. |
||||||
|
* |
||||||
|
* When we know the jsr target<->ret correlation, we promote from the |
||||||
|
* nextByAddr of every jsr the locals that are accessed by the |
||||||
|
* subroutine to the corresponding ret and the others to the jsr. Also |
||||||
|
* we will promote all reads from the jsr targets to the jsr. |
||||||
|
* |
||||||
|
* If you think this might be to complicated, keep in mind that jsr's |
||||||
|
* are not only left by the ret instructions, but also "spontanously" |
||||||
|
* (by not reading the return address again). |
||||||
|
*/ |
||||||
|
public class LocalOptimizer implements Opcodes, CodeTransformer { |
||||||
|
|
||||||
|
/** |
||||||
|
* This class keeps track of which locals must be the same, which |
||||||
|
* name and type each local (if there is a local variable table) and |
||||||
|
* which other locals have an intersecting life time. |
||||||
|
*/ |
||||||
|
class LocalInfo { |
||||||
|
LocalInfo shadow = null; |
||||||
|
|
||||||
|
public LocalInfo getReal() { |
||||||
|
LocalInfo real = this; |
||||||
|
while (real.shadow != null) |
||||||
|
real = real.shadow; |
||||||
|
return real; |
||||||
|
} |
||||||
|
|
||||||
|
String name; |
||||||
|
String type; |
||||||
|
Vector usingInstrs = new Vector(); |
||||||
|
Vector conflictingLocals = new Vector(); |
||||||
|
int size; |
||||||
|
int newSlot = -1; |
||||||
|
|
||||||
|
LocalInfo() { |
||||||
|
} |
||||||
|
|
||||||
|
LocalInfo(InstrInfo instr) { |
||||||
|
usingInstrs.addElement(instr); |
||||||
|
} |
||||||
|
|
||||||
|
void conflictsWith(LocalInfo l) { |
||||||
|
if (shadow != null) { |
||||||
|
getReal().conflictsWith(l); |
||||||
|
} else { |
||||||
|
l = l.getReal(); |
||||||
|
if (!conflictingLocals.contains(l)) { |
||||||
|
conflictingLocals.addElement(l); |
||||||
|
l.conflictingLocals.addElement(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void combineInto(LocalInfo l) { |
||||||
|
if (shadow != null) { |
||||||
|
getReal().combineInto(l); |
||||||
|
return; |
||||||
|
} |
||||||
|
l = l.getReal(); |
||||||
|
if (this == l) |
||||||
|
return; |
||||||
|
shadow = l; |
||||||
|
if (shadow.name == null) { |
||||||
|
shadow.name = name; |
||||||
|
shadow.type = type; |
||||||
|
} |
||||||
|
Enumeration enum = usingInstrs.elements(); |
||||||
|
while (enum.hasMoreElements()) { |
||||||
|
InstrInfo instr = (InstrInfo) enum.nextElement(); |
||||||
|
instr.local = l; |
||||||
|
l.usingInstrs.addElement(instr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int getFirstAddr() { |
||||||
|
int minAddr = Integer.MAX_VALUE; |
||||||
|
Enumeration enum = usingInstrs.elements(); |
||||||
|
while (enum.hasMoreElements()) { |
||||||
|
InstrInfo info = (InstrInfo) enum.nextElement(); |
||||||
|
if (info.instr.getAddr() < minAddr) |
||||||
|
minAddr = info.instr.getAddr(); |
||||||
|
} |
||||||
|
return minAddr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class TodoQueue { |
||||||
|
public final InstrInfo LAST = new InstrInfo(); |
||||||
|
InstrInfo first = LAST; |
||||||
|
|
||||||
|
public void add(InstrInfo info) { |
||||||
|
if (info.nextTodo == null) { |
||||||
|
/* only enqueue if not already on queue */ |
||||||
|
info.nextTodo = first; |
||||||
|
first = info; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isEmpty() { |
||||||
|
return first == LAST; |
||||||
|
} |
||||||
|
|
||||||
|
public InstrInfo remove() { |
||||||
|
if (first == LAST) |
||||||
|
throw new NoSuchElementException(); |
||||||
|
InstrInfo result = first; |
||||||
|
first = result.nextTodo; |
||||||
|
result.nextTodo = null; |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* This class contains information for each instruction. |
||||||
|
*/ |
||||||
|
static class InstrInfo { |
||||||
|
/** |
||||||
|
* The next changed InstrInfo, or null, if this instr info did |
||||||
|
* not changed. |
||||||
|
*/ |
||||||
|
InstrInfo nextTodo; |
||||||
|
|
||||||
|
/** |
||||||
|
* The LocalInfo that this instruction manipulates, or null |
||||||
|
* if this is not an ret, iinc, load or store instruction. |
||||||
|
*/ |
||||||
|
LocalInfo local; |
||||||
|
/** |
||||||
|
* For each slot, this contains the InstrInfo of one of the |
||||||
|
* next Instruction, that may read from that slot, without |
||||||
|
* prior writing. */ |
||||||
|
InstrInfo[] nextReads; |
||||||
|
|
||||||
|
/** |
||||||
|
* This only has a value for ret instructions. In that case |
||||||
|
* this bitset contains all locals, that may be used between |
||||||
|
* jsr and ret. |
||||||
|
*/ |
||||||
|
BitSet usedBySub; |
||||||
|
/** |
||||||
|
* For each slot if get() is true, no instruction may read |
||||||
|
* this slot, since it may contain different locals, depending |
||||||
|
* on flow. |
||||||
|
*/ |
||||||
|
LocalInfo[] lifeLocals; |
||||||
|
/** |
||||||
|
* If instruction is the destination of a jsr, this contains |
||||||
|
* the single allowed ret instruction info, or null if there |
||||||
|
* is no ret at all (or not yet detected). |
||||||
|
*/ |
||||||
|
InstrInfo retInfo; |
||||||
|
/** |
||||||
|
* If this instruction is a ret, this contains the single |
||||||
|
* allowed jsr target to which this ret belongs. |
||||||
|
*/ |
||||||
|
InstrInfo jsrTargetInfo; |
||||||
|
/** |
||||||
|
* The Instruction of this info |
||||||
|
*/ |
||||||
|
Instruction instr; |
||||||
|
/** |
||||||
|
* The next info in the chain. |
||||||
|
*/ |
||||||
|
InstrInfo nextInfo; |
||||||
|
} |
||||||
|
|
||||||
|
BytecodeInfo bc; |
||||||
|
|
||||||
|
TodoQueue changedInfos; |
||||||
|
InstrInfo firstInfo; |
||||||
|
Hashtable instrInfos; |
||||||
|
boolean produceLVT; |
||||||
|
int maxlocals; |
||||||
|
|
||||||
|
LocalInfo[] paramLocals; |
||||||
|
|
||||||
|
public LocalOptimizer() { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 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, |
||||||
|
BitSet mergeSet, boolean inverted) { |
||||||
|
InstrInfo preInfo = (InstrInfo) instrInfos.get(preInstr); |
||||||
|
int omitLocal = -1; |
||||||
|
if (preInstr.getOpcode() >= opc_istore |
||||||
|
&& preInstr.getOpcode() <= opc_astore) { |
||||||
|
/* This is a store */ |
||||||
|
omitLocal = preInstr.getLocalSlot(); |
||||||
|
if (info.nextReads[omitLocal] != null) |
||||||
|
preInfo.local.combineInto(info.nextReads[omitLocal].local); |
||||||
|
} |
||||||
|
for (int i=0; i < maxlocals; i++) { |
||||||
|
if (info.nextReads[i] != null && i != omitLocal |
||||||
|
&& (mergeSet == null || mergeSet.get(i) != inverted)) { |
||||||
|
|
||||||
|
if (preInfo.nextReads[i] == null) { |
||||||
|
preInfo.nextReads[i] = info.nextReads[i]; |
||||||
|
changedInfos.add(preInfo); |
||||||
|
} else { |
||||||
|
preInfo.nextReads[i].local |
||||||
|
.combineInto(info.nextReads[i].local); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void promoteReads(InstrInfo info, Instruction preInstr) { |
||||||
|
promoteReads(info, preInstr, null, false); |
||||||
|
} |
||||||
|
|
||||||
|
public LocalVariableInfo findLVTEntry(LocalVariableInfo[] lvt, |
||||||
|
int slot, int addr) { |
||||||
|
LocalVariableInfo match = null; |
||||||
|
for (int i=0; i < lvt.length; i++) { |
||||||
|
if (lvt[i].slot == slot |
||||||
|
&& lvt[i].start.getAddr() <= addr |
||||||
|
&& lvt[i].end.getAddr() >= addr) { |
||||||
|
if (match != null |
||||||
|
&& (!match.name.equals(lvt[i].name) |
||||||
|
|| !match.type.equals(lvt[i].type))) { |
||||||
|
/* Multiple matches..., give no info */ |
||||||
|
return null; |
||||||
|
} |
||||||
|
match = lvt[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
return match; |
||||||
|
} |
||||||
|
|
||||||
|
public LocalVariableInfo findLVTEntry(LocalVariableInfo[] lvt, |
||||||
|
Instruction instr) { |
||||||
|
int addr; |
||||||
|
if (instr.getOpcode() >= opc_istore |
||||||
|
&& instr.getOpcode() <= opc_astore) |
||||||
|
addr = instr.getNextAddr(); |
||||||
|
else |
||||||
|
addr = instr.getAddr(); |
||||||
|
return findLVTEntry(lvt, instr.getLocalSlot(), addr); |
||||||
|
} |
||||||
|
|
||||||
|
public void calcLocalInfo() { |
||||||
|
maxlocals = bc.getMaxLocals(); |
||||||
|
Handler[] handlers = bc.getExceptionHandlers(); |
||||||
|
LocalVariableInfo[] lvt = bc.getLocalVariableTable(); |
||||||
|
if (lvt != null) |
||||||
|
produceLVT = true; |
||||||
|
|
||||||
|
/* Initialize paramLocals */ |
||||||
|
{ |
||||||
|
String methodType = bc.getMethodInfo().getType(); |
||||||
|
int paramCount = (bc.getMethodInfo().isStatic() ? 0 : 1) |
||||||
|
+ TypeSignature.getArgumentSize(methodType); |
||||||
|
paramLocals = new LocalInfo[paramCount]; |
||||||
|
int slot = 0; |
||||||
|
if (!bc.getMethodInfo().isStatic()) { |
||||||
|
LocalInfo local = new LocalInfo(); |
||||||
|
if (lvt != null) { |
||||||
|
LocalVariableInfo lvi = findLVTEntry(lvt, 0, 0); |
||||||
|
if (lvi != null) { |
||||||
|
local.name = lvi.name; |
||||||
|
local.type = lvi.type; |
||||||
|
} |
||||||
|
} |
||||||
|
local.size = 1; |
||||||
|
paramLocals[slot++] = local; |
||||||
|
} |
||||||
|
int pos = 1; |
||||||
|
while (pos < methodType.length() |
||||||
|
&& methodType.charAt(pos) != ')') { |
||||||
|
|
||||||
|
LocalInfo local = new LocalInfo(); |
||||||
|
if (lvt != null) { |
||||||
|
LocalVariableInfo lvi = findLVTEntry(lvt, slot, 0); |
||||||
|
if (lvi != null) { |
||||||
|
local.name = lvi.name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int start = pos; |
||||||
|
pos = TypeSignature.skipType(methodType, pos); |
||||||
|
local.type = methodType.substring(start, pos); |
||||||
|
local.size = TypeSignature.getTypeSize(local.type); |
||||||
|
paramLocals[slot] = local; |
||||||
|
slot += local.size; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Initialize the InstrInfos and LocalInfos |
||||||
|
*/ |
||||||
|
changedInfos = new TodoQueue(); |
||||||
|
instrInfos = new Hashtable(); |
||||||
|
{ |
||||||
|
InstrInfo info = firstInfo = new InstrInfo(); |
||||||
|
Iterator i = bc.getInstructions().iterator(); |
||||||
|
while (true) { |
||||||
|
Instruction instr = (Instruction) i.next(); |
||||||
|
instrInfos.put(instr, info); |
||||||
|
info.instr = instr; |
||||||
|
info.nextReads = new InstrInfo[maxlocals]; |
||||||
|
if (instr.hasLocalSlot()) { |
||||||
|
info.local = new LocalInfo(info); |
||||||
|
if (lvt != null) { |
||||||
|
LocalVariableInfo lvi = findLVTEntry(lvt, instr); |
||||||
|
if (lvi != null) { |
||||||
|
info.local.name = lvi.name; |
||||||
|
info.local.type = lvi.type; |
||||||
|
} |
||||||
|
} |
||||||
|
info.local.size = 1; |
||||||
|
switch (instr.getOpcode()) { |
||||||
|
case opc_lload: case opc_dload: |
||||||
|
info.local.size = 2; |
||||||
|
/* fall through */ |
||||||
|
case opc_iload: case opc_fload: case opc_aload: |
||||||
|
case opc_iinc: |
||||||
|
/* this is a load instruction */ |
||||||
|
info.nextReads[instr.getLocalSlot()] = info; |
||||||
|
changedInfos.add(info); |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_ret: |
||||||
|
/* this is a ret instruction */ |
||||||
|
info.usedBySub = new BitSet(); |
||||||
|
info.nextReads[instr.getLocalSlot()] = info; |
||||||
|
changedInfos.add(info); |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_lstore: case opc_dstore: |
||||||
|
info.local.size = 2; |
||||||
|
//case opc_istore: case opc_fstore: case opc_astore:
|
||||||
|
} |
||||||
|
} |
||||||
|
if (!i.hasNext()) |
||||||
|
break; |
||||||
|
info = info.nextInfo = new InstrInfo(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* find out which locals are the same. |
||||||
|
*/ |
||||||
|
while (!changedInfos.isEmpty()) { |
||||||
|
InstrInfo info = changedInfos.remove(); |
||||||
|
Instruction instr = info.instr; |
||||||
|
|
||||||
|
/* Mark the local as used in all ret instructions */ |
||||||
|
if (instr.hasLocalSlot()) { |
||||||
|
int slot = instr.getLocalSlot(); |
||||||
|
for (int i=0; i< maxlocals; i++) { |
||||||
|
InstrInfo retInfo = info.nextReads[i]; |
||||||
|
if (retInfo != null |
||||||
|
&& retInfo.instr.getOpcode() == opc_ret |
||||||
|
&& !retInfo.usedBySub.get(slot)) { |
||||||
|
retInfo.usedBySub.set(slot); |
||||||
|
if (retInfo.jsrTargetInfo != null) |
||||||
|
changedInfos.add(retInfo.jsrTargetInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Instruction prevInstr = instr.getPrevByAddr(); |
||||||
|
if (prevInstr != null) { |
||||||
|
if (!prevInstr.doesAlwaysJump()) |
||||||
|
promoteReads(info, prevInstr); |
||||||
|
else if (prevInstr.getOpcode() == opc_jsr) { |
||||||
|
/* Prev instr is a jsr, promote reads to the |
||||||
|
* corresponding ret. |
||||||
|
*/ |
||||||
|
InstrInfo jsrInfo = |
||||||
|
(InstrInfo) instrInfos.get(prevInstr.getSingleSucc()); |
||||||
|
if (jsrInfo.retInfo != null) { |
||||||
|
/* Now promote reads that are modified by the |
||||||
|
* subroutine to the ret, and those that are not |
||||||
|
* to the jsr instruction. |
||||||
|
*/ |
||||||
|
promoteReads(info, jsrInfo.retInfo.instr, |
||||||
|
jsrInfo.retInfo.usedBySub, false); |
||||||
|
promoteReads(info, prevInstr, |
||||||
|
jsrInfo.retInfo.usedBySub, true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (instr.getPreds() != null) { |
||||||
|
for (int i = 0; i < instr.getPreds().length; i++) { |
||||||
|
Instruction predInstr = instr.getPreds()[i]; |
||||||
|
if (instr.getPreds()[i].getOpcode() == opc_jsr) { |
||||||
|
/* This is the target of a jsr instr. |
||||||
|
*/ |
||||||
|
if (info.instr.getOpcode() != opc_astore) { |
||||||
|
/* XXX Grrr, the bytecode verifier doesn't |
||||||
|
* test if a jsr starts with astore. So |
||||||
|
* it is possible to do something else |
||||||
|
* before putting the ret address into a |
||||||
|
* local. */ |
||||||
|
throw new AssertError("Non standard jsr"); |
||||||
|
} |
||||||
|
InstrInfo retInfo = info.nextInfo.nextReads |
||||||
|
[info.instr.getLocalSlot()]; |
||||||
|
|
||||||
|
if (retInfo != null) { |
||||||
|
if (retInfo.instr.getOpcode() != opc_ret) |
||||||
|
throw new AssertError |
||||||
|
("reading return address"); |
||||||
|
|
||||||
|
info.retInfo = retInfo; |
||||||
|
retInfo.jsrTargetInfo = info; |
||||||
|
|
||||||
|
/* Now promote reads from the instruction |
||||||
|
* after the jsr to the ret instruction if |
||||||
|
* they are modified by the subroutine, |
||||||
|
* and to the jsr instruction otherwise. |
||||||
|
*/ |
||||||
|
Instruction nextInstr = predInstr.getNextByAddr(); |
||||||
|
InstrInfo nextInfo |
||||||
|
= (InstrInfo) instrInfos.get(nextInstr); |
||||||
|
|
||||||
|
promoteReads(nextInfo, retInfo.instr, |
||||||
|
retInfo.usedBySub, false); |
||||||
|
|
||||||
|
promoteReads(nextInfo, predInstr, |
||||||
|
retInfo.usedBySub, true); |
||||||
|
} |
||||||
|
} |
||||||
|
promoteReads(info, instr.getPreds()[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (int i=0; i < handlers.length; i++) { |
||||||
|
if (handlers[i].catcher == instr) { |
||||||
|
for (Instruction preInstr = handlers[i].start; |
||||||
|
preInstr != handlers[i].end.getNextByAddr(); |
||||||
|
preInstr = preInstr.getNextByAddr()) { |
||||||
|
promoteReads(info, preInstr); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
changedInfos = null; |
||||||
|
|
||||||
|
/* Now merge with the parameters |
||||||
|
* The params should be the locals in firstInfo.nextReads |
||||||
|
*/ |
||||||
|
for (int i=0; i< paramLocals.length; i++) { |
||||||
|
if (firstInfo.nextReads[i] != null) { |
||||||
|
firstInfo.nextReads[i].local.combineInto(paramLocals[i]); |
||||||
|
paramLocals[i] = paramLocals[i].getReal(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void stripLocals() { |
||||||
|
ListIterator iter = bc.getInstructions().listIterator(); |
||||||
|
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { |
||||||
|
Instruction instr = (Instruction) iter.next(); |
||||||
|
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 (instr.getOpcode()) { |
||||||
|
case opc_istore: |
||||||
|
case opc_fstore: |
||||||
|
case opc_astore: |
||||||
|
iter.set(new Instruction(opc_pop)); |
||||||
|
break; |
||||||
|
case opc_lstore: |
||||||
|
case opc_dstore: |
||||||
|
iter.set(new Instruction(opc_pop2)); |
||||||
|
break; |
||||||
|
default: |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void distributeLocals(Vector locals) { |
||||||
|
if (locals.size() == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
/* Find the local with the least conflicts. */ |
||||||
|
int min = Integer.MAX_VALUE; |
||||||
|
LocalInfo bestLocal = null; |
||||||
|
Enumeration enum = locals.elements(); |
||||||
|
while (enum.hasMoreElements()) { |
||||||
|
LocalInfo li = (LocalInfo) enum.nextElement(); |
||||||
|
int conflicts = 0; |
||||||
|
Enumeration conflenum = li.conflictingLocals.elements(); |
||||||
|
while (conflenum.hasMoreElements()) { |
||||||
|
if (((LocalInfo)conflenum.nextElement()).newSlot != -2) |
||||||
|
conflicts++; |
||||||
|
} |
||||||
|
if (conflicts < min) { |
||||||
|
min = conflicts; |
||||||
|
bestLocal = li; |
||||||
|
} |
||||||
|
} |
||||||
|
/* Mark the local as taken */ |
||||||
|
locals.removeElement(bestLocal); |
||||||
|
bestLocal.newSlot = -2; |
||||||
|
/* Now distribute the remaining locals recursively. */ |
||||||
|
distributeLocals(locals); |
||||||
|
|
||||||
|
/* Finally find a new slot */ |
||||||
|
next_slot: |
||||||
|
for (int slot = 0; ; slot++) { |
||||||
|
Enumeration conflenum = bestLocal.conflictingLocals.elements(); |
||||||
|
while (conflenum.hasMoreElements()) { |
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void distributeLocals() { |
||||||
|
/* give locals new slots. This is a graph coloring |
||||||
|
* algorithm (the optimal solution is NP complete, but this |
||||||
|
* should be a good approximation). |
||||||
|
*/ |
||||||
|
|
||||||
|
/* first give the params the same slot as they had before. |
||||||
|
*/ |
||||||
|
for (int i=0; i<paramLocals.length; i++) |
||||||
|
if (paramLocals[i] != null) |
||||||
|
paramLocals[i].newSlot = i; |
||||||
|
|
||||||
|
/* Now calculate the conflict settings. |
||||||
|
*/ |
||||||
|
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { |
||||||
|
if (info.instr.getOpcode() >= BytecodeInfo.opc_istore |
||||||
|
&& info.instr.getOpcode() <= BytecodeInfo.opc_astore) { |
||||||
|
/* This is a store. It conflicts with every local, whose |
||||||
|
* value will be read without write. |
||||||
|
* |
||||||
|
* If this is inside a ret, it also conflicts with |
||||||
|
* locals, that are not used inside, and where any jsr |
||||||
|
* would conflict with. |
||||||
|
*/ |
||||||
|
for (int i=0; i < maxlocals; i++) { |
||||||
|
if (i != info.instr.getLocalSlot() |
||||||
|
&& info.nextReads[i] != null) |
||||||
|
info.local.conflictsWith(info.nextReads[i].local); |
||||||
|
if (info.nextInfo.nextReads[i] != null |
||||||
|
&& info.nextInfo.nextReads[i].jsrTargetInfo != null) { |
||||||
|
Instruction[] jsrs = info.nextInfo.nextReads[i] |
||||||
|
.jsrTargetInfo.instr.getPreds(); |
||||||
|
for (int j=0; j< jsrs.length; j++) { |
||||||
|
InstrInfo jsrInfo |
||||||
|
= (InstrInfo) instrInfos.get(jsrs[j]); |
||||||
|
for (int k=0; k < maxlocals; k++) { |
||||||
|
if (!info.nextInfo.nextReads[i].usedBySub |
||||||
|
.get(k) |
||||||
|
&& jsrInfo.nextReads[k] != null) |
||||||
|
info.local.conflictsWith |
||||||
|
(jsrInfo.nextReads[k].local); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Now put the locals that need a color into a vector. |
||||||
|
*/ |
||||||
|
Vector locals = new Vector(); |
||||||
|
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { |
||||||
|
if (info.local != null |
||||||
|
&& info.local.newSlot == -1 |
||||||
|
&& !locals.contains(info.local)) |
||||||
|
locals.addElement(info.local); |
||||||
|
} |
||||||
|
|
||||||
|
/* Now distribute slots recursive. |
||||||
|
*/ |
||||||
|
distributeLocals(locals); |
||||||
|
|
||||||
|
/* Update the instructions and calculate new maxlocals. |
||||||
|
*/ |
||||||
|
maxlocals = paramLocals.length; |
||||||
|
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { |
||||||
|
if (info.local != null) { |
||||||
|
if (info.local.newSlot+info.local.size > maxlocals) |
||||||
|
maxlocals = info.local.newSlot + info.local.size; |
||||||
|
info.instr.setLocalSlot(info.local.newSlot); |
||||||
|
} |
||||||
|
} |
||||||
|
bc.setMaxLocals(maxlocals); |
||||||
|
|
||||||
|
/* Update LocalVariableTable |
||||||
|
*/ |
||||||
|
if (produceLVT) |
||||||
|
buildNewLVT(); |
||||||
|
} |
||||||
|
|
||||||
|
private InstrInfo CONFLICT = new InstrInfo(); |
||||||
|
|
||||||
|
boolean promoteLifeLocals(LocalInfo[] newLife, InstrInfo nextInfo) { |
||||||
|
if (nextInfo.lifeLocals == null) { |
||||||
|
nextInfo.lifeLocals = (LocalInfo[]) newLife.clone(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
boolean changed = false; |
||||||
|
for (int i=0; i< maxlocals; i++) { |
||||||
|
LocalInfo local = nextInfo.lifeLocals[i]; |
||||||
|
if (local == null) |
||||||
|
/* A conflict has already happened, or this slot |
||||||
|
* may not have been initialized. */ |
||||||
|
continue; |
||||||
|
|
||||||
|
local = local.getReal(); |
||||||
|
LocalInfo newLocal = newLife[i]; |
||||||
|
if (newLocal != null) |
||||||
|
newLocal = newLocal.getReal(); |
||||||
|
if (local != newLocal) { |
||||||
|
nextInfo.lifeLocals[i] = null; |
||||||
|
changed = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return changed; |
||||||
|
} |
||||||
|
|
||||||
|
public void buildNewLVT() { |
||||||
|
/* First we recalculate the usedBySub, to use the new local numbers. |
||||||
|
*/ |
||||||
|
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) |
||||||
|
if (info.usedBySub != null) |
||||||
|
info.usedBySub = new BitSet(); |
||||||
|
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { |
||||||
|
if (info.local != null) { |
||||||
|
for (int i=0; i < info.nextReads.length; i++) { |
||||||
|
if (info.nextReads[i] != null |
||||||
|
&& info.nextReads[i].instr.getOpcode() == opc_ret) |
||||||
|
info.nextReads[i].usedBySub.set(info.local.newSlot); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Now we begin with the first Instruction and follow program flow. |
||||||
|
* We remember which locals are life in lifeLocals. |
||||||
|
*/ |
||||||
|
|
||||||
|
firstInfo.lifeLocals = new LocalInfo[maxlocals]; |
||||||
|
for (int i=0; i < paramLocals.length; i++) |
||||||
|
firstInfo.lifeLocals[i] = paramLocals[i]; |
||||||
|
|
||||||
|
Stack changedInfo = new Stack(); |
||||||
|
changedInfo.push(firstInfo); |
||||||
|
Handler[] handlers = bc.getExceptionHandlers(); |
||||||
|
while (!changedInfo.isEmpty()) { |
||||||
|
InstrInfo info = (InstrInfo) changedInfo.pop(); |
||||||
|
Instruction instr = info.instr; |
||||||
|
LocalInfo[] newLife = info.lifeLocals; |
||||||
|
if (instr.hasLocalSlot()) { |
||||||
|
int slot = instr.getLocalSlot(); |
||||||
|
LocalInfo instrLocal = info.local.getReal(); |
||||||
|
newLife = (LocalInfo[]) newLife.clone(); |
||||||
|
newLife[slot] = instrLocal; |
||||||
|
if (instrLocal.name != null) { |
||||||
|
for (int j=0; j< newLife.length; j++) { |
||||||
|
if (j != slot |
||||||
|
&& newLife[j] != null |
||||||
|
&& instrLocal.name.equals(newLife[j].name)) { |
||||||
|
/* This local changed the slot. */ |
||||||
|
newLife[j] = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!instr.doesAlwaysJump()) { |
||||||
|
InstrInfo nextInfo = info.nextInfo; |
||||||
|
if (promoteLifeLocals(newLife, nextInfo)) |
||||||
|
changedInfo.push(nextInfo); |
||||||
|
} |
||||||
|
if (instr.hasSuccs()) { |
||||||
|
Instruction[] succs = instr.getSuccs(); |
||||||
|
for (int i = 0; i < succs.length; i++) { |
||||||
|
InstrInfo nextInfo |
||||||
|
= (InstrInfo) instrInfos.get(succs[i]); |
||||||
|
if (promoteLifeLocals(newLife, nextInfo)) |
||||||
|
changedInfo.push(nextInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
for (int i=0; i < handlers.length; i++) { |
||||||
|
if (handlers[i].start.compareTo(instr) <= 0 |
||||||
|
&& handlers[i].end.compareTo(instr) >= 0) { |
||||||
|
InstrInfo nextInfo |
||||||
|
= (InstrInfo) instrInfos.get(handlers[i].catcher); |
||||||
|
if (promoteLifeLocals(newLife, nextInfo)) |
||||||
|
changedInfo.push(nextInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (info.instr.getOpcode() == opc_jsr) { |
||||||
|
/* On a jsr we do a special merge */ |
||||||
|
|
||||||
|
Instruction jsrTargetInstr = info.instr.getSingleSucc(); |
||||||
|
InstrInfo jsrTargetInfo |
||||||
|
= (InstrInfo) instrInfos.get(jsrTargetInstr); |
||||||
|
InstrInfo retInfo = jsrTargetInfo.retInfo; |
||||||
|
if (retInfo != null && retInfo.lifeLocals != null) { |
||||||
|
LocalInfo[] retLife = (LocalInfo[]) newLife.clone(); |
||||||
|
for (int i=0; i< maxlocals; i++) { |
||||||
|
if (retInfo.usedBySub.get(i)) |
||||||
|
retLife[i] = retInfo.lifeLocals[i]; |
||||||
|
} |
||||||
|
if (promoteLifeLocals(retLife, info.nextInfo)) |
||||||
|
changedInfo.push(info.nextInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (info.jsrTargetInfo != null) { |
||||||
|
/* On a ret we do a special merge */ |
||||||
|
|
||||||
|
Instruction jsrTargetInstr = info.jsrTargetInfo.instr; |
||||||
|
for (int j=0; j< jsrTargetInstr.getPreds().length; j++) { |
||||||
|
InstrInfo jsrInfo |
||||||
|
= (InstrInfo) instrInfos.get(jsrTargetInstr.getPreds()[j]); |
||||||
|
|
||||||
|
if (jsrInfo.lifeLocals == null) |
||||||
|
/* life locals are not calculated, yet */ |
||||||
|
continue; |
||||||
|
LocalInfo[] retLife = (LocalInfo[]) newLife.clone(); |
||||||
|
for (int i=0; i< maxlocals; i++) { |
||||||
|
if (!info.usedBySub.get(i)) |
||||||
|
retLife[i] = jsrInfo.lifeLocals[i]; |
||||||
|
} |
||||||
|
if (promoteLifeLocals(retLife, jsrInfo.nextInfo)) |
||||||
|
changedInfo.push(jsrInfo.nextInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Vector lvtEntries = new Vector(); |
||||||
|
LocalVariableInfo[] lvi = new LocalVariableInfo[maxlocals]; |
||||||
|
LocalInfo[] currentLocal = new LocalInfo[maxlocals]; |
||||||
|
for (int i=0; i< paramLocals.length; i++) { |
||||||
|
if (paramLocals[i] != null) { |
||||||
|
currentLocal[i] = paramLocals[i]; |
||||||
|
if (currentLocal[i].name != null) { |
||||||
|
lvi[i] = new LocalVariableInfo(); |
||||||
|
lvtEntries.addElement(lvi[i]); |
||||||
|
lvi[i].name = currentLocal[i].name; /* XXX obfuscation? */ |
||||||
|
lvi[i].type = Main.getClassBundle() |
||||||
|
.getTypeAlias(currentLocal[i].type); |
||||||
|
lvi[i].start = (Instruction) bc.getInstructions().get(0); |
||||||
|
lvi[i].slot = i; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Instruction lastInstr = null; |
||||||
|
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { |
||||||
|
for (int i=0; i< maxlocals; i++) { |
||||||
|
LocalInfo lcl = info.lifeLocals != null ? info.lifeLocals[i] |
||||||
|
: null; |
||||||
|
if (lcl != currentLocal[i] |
||||||
|
&& (lcl == null || currentLocal[i] == null |
||||||
|
|| lcl.name == null || lcl.type == null |
||||||
|
|| !lcl.name.equals(currentLocal[i].name) |
||||||
|
|| !lcl.type.equals(currentLocal[i].type))) { |
||||||
|
if (lvi[i] != null) { |
||||||
|
lvi[i].end = info.instr.getPrevByAddr(); |
||||||
|
} |
||||||
|
lvi[i] = null; |
||||||
|
currentLocal[i] = lcl; |
||||||
|
if (currentLocal[i] != null |
||||||
|
&& currentLocal[i].name != null |
||||||
|
&& currentLocal[i].type != null) { |
||||||
|
lvi[i] = new LocalVariableInfo(); |
||||||
|
lvtEntries.addElement(lvi[i]); |
||||||
|
lvi[i].name = currentLocal[i].name; |
||||||
|
lvi[i].type = Main.getClassBundle() |
||||||
|
.getTypeAlias(currentLocal[i].type); |
||||||
|
lvi[i].start = info.instr; |
||||||
|
lvi[i].slot = i; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
lastInstr = info.instr; |
||||||
|
} |
||||||
|
for (int i=0; i< maxlocals; i++) { |
||||||
|
if (lvi[i] != null) |
||||||
|
lvi[i].end = lastInstr; |
||||||
|
} |
||||||
|
LocalVariableInfo[] lvt = new LocalVariableInfo[lvtEntries.size()]; |
||||||
|
lvtEntries.copyInto(lvt); |
||||||
|
bc.setLocalVariableTable(lvt); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpLocals() { |
||||||
|
Vector locals = new Vector(); |
||||||
|
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { |
||||||
|
GlobalOptions.err.println(info.instr.getDescription()); |
||||||
|
GlobalOptions.err.print("nextReads: "); |
||||||
|
for (int i=0; i<maxlocals; i++) |
||||||
|
if (info.nextReads[i] == null) |
||||||
|
GlobalOptions.err.print("-,"); |
||||||
|
else |
||||||
|
GlobalOptions.err.print(info.nextReads[i].instr.getAddr()+","); |
||||||
|
if (info.usedBySub != null) |
||||||
|
GlobalOptions.err.print(" usedBySub: "+info.usedBySub); |
||||||
|
if (info.retInfo != null) |
||||||
|
GlobalOptions.err.print(" ret info: " |
||||||
|
+info.retInfo.instr.getAddr()); |
||||||
|
if (info.jsrTargetInfo != null) |
||||||
|
GlobalOptions.err.print(" jsr info: " |
||||||
|
+info.jsrTargetInfo.instr.getAddr()); |
||||||
|
|
||||||
|
GlobalOptions.err.println(); |
||||||
|
if (info.local != null && !locals.contains(info.local)) |
||||||
|
locals.addElement(info.local); |
||||||
|
} |
||||||
|
Enumeration enum = locals.elements(); |
||||||
|
while (enum.hasMoreElements()) { |
||||||
|
LocalInfo li = (LocalInfo) enum.nextElement(); |
||||||
|
int slot = ((InstrInfo)li.usingInstrs.elementAt(0)) |
||||||
|
.instr.getLocalSlot(); |
||||||
|
GlobalOptions.err.print("Slot: "+slot+" conflicts:"); |
||||||
|
Enumeration enum1 = li.conflictingLocals.elements(); |
||||||
|
while (enum1.hasMoreElements()) { |
||||||
|
LocalInfo cfl = (LocalInfo)enum1.nextElement(); |
||||||
|
GlobalOptions.err.print(cfl.getFirstAddr()+", "); |
||||||
|
} |
||||||
|
GlobalOptions.err.println(); |
||||||
|
GlobalOptions.err.print(li.getFirstAddr()); |
||||||
|
GlobalOptions.err.print(" instrs: "); |
||||||
|
Enumeration enum2 = li.usingInstrs.elements(); |
||||||
|
while (enum2.hasMoreElements()) |
||||||
|
GlobalOptions.err.print(((InstrInfo)enum2.nextElement()) |
||||||
|
.instr.getAddr()+", "); |
||||||
|
GlobalOptions.err.println(); |
||||||
|
} |
||||||
|
GlobalOptions.err.println("-----------"); |
||||||
|
} |
||||||
|
|
||||||
|
public void transformCode(BytecodeInfo bytecode) { |
||||||
|
this.bc = bytecode; |
||||||
|
calcLocalInfo(); |
||||||
|
if ((GlobalOptions.debuggingFlags |
||||||
|
& GlobalOptions.DEBUG_LOCALS) != 0) { |
||||||
|
GlobalOptions.err.println("Before Local Optimization: "); |
||||||
|
dumpLocals(); |
||||||
|
} |
||||||
|
stripLocals(); |
||||||
|
distributeLocals(); |
||||||
|
|
||||||
|
if ((GlobalOptions.debuggingFlags |
||||||
|
& GlobalOptions.DEBUG_LOCALS) != 0) { |
||||||
|
GlobalOptions.err.println("After Local Optimization: "); |
||||||
|
dumpLocals(); |
||||||
|
} |
||||||
|
|
||||||
|
firstInfo = null; |
||||||
|
changedInfos = null; |
||||||
|
instrInfos = null; |
||||||
|
paramLocals = null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/* LocalizeFieldTransformer 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.modules; |
||||||
|
import jode.obfuscator.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class replaces accesses to local fields and . |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class LocalizeFieldTransformer implements CodeTransformer { |
||||||
|
|
||||||
|
public static void transformCode(BytecodeInfo bytecode) { |
||||||
|
for (Instruction instr = bytecode.getFirstInstr(); |
||||||
|
instr != null; instr = instr.nextByAddr) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
## Input file for automake to generate the Makefile.in used by configure
|
||||||
|
|
||||||
|
JAR = @JAR@
|
||||||
|
JAVAC = @JAVAC@
|
||||||
|
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\
|
||||||
|
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
|
||||||
|
-depfile=Makefile.dep
|
||||||
|
CLASSPATH = @CLASSPATH@
|
||||||
|
CLASSLIB = @CLASSLIB@
|
||||||
|
SUBSTCP = @SUBSTCP@
|
||||||
|
BUILD_CLASSPATH = $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB)
|
||||||
|
|
||||||
|
MY_JAVA_FILES = \
|
||||||
|
ConstantAnalyzer.java \
|
||||||
|
KeywordRenamer.java \
|
||||||
|
LocalOptimizer.java \
|
||||||
|
ModifierMatcher.java \
|
||||||
|
MultiIdentifierMatcher.java \
|
||||||
|
NameSwapper.java \
|
||||||
|
RemovePopAnalyzer.java \
|
||||||
|
SerializePreserver.java \
|
||||||
|
SimpleAnalyzer.java \
|
||||||
|
StrongRenamer.java \
|
||||||
|
UniqueRenamer.java \
|
||||||
|
WildCard.java
|
||||||
|
# LocalizeFieldTransformer.java
|
||||||
|
|
||||||
|
noinst_DATA = $(MY_JAVA_FILES:.java=.class)
|
||||||
|
EXTRA_DIST = $(MY_JAVA_FILES)
|
||||||
|
|
||||||
|
@QUOTE@-include Makefile.dep |
||||||
|
|
||||||
|
%.class: %.java |
||||||
|
$(JAVAC) -classpath `$(SUBSTCP) $(BUILD_CLASSPATH):$(CLASSLIB)` -d $(top_builddir) $<
|
||||||
|
|
||||||
|
Makefile.dep: $(MY_JAVA_FILES:.java=.class) |
||||||
|
$(JAVADEP) $^
|
||||||
|
|
||||||
|
clean-local: |
||||||
|
@rm -f *.class
|
||||||
|
@rm -f *.dep
|
@ -0,0 +1,350 @@ |
|||||||
|
/* ModifierMatcher 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.modules; |
||||||
|
import jode.obfuscator.IdentifierMatcher; |
||||||
|
import jode.obfuscator.Identifier; |
||||||
|
import jode.obfuscator.ClassIdentifier; |
||||||
|
import jode.obfuscator.FieldIdentifier; |
||||||
|
import jode.obfuscator.MethodIdentifier; |
||||||
|
|
||||||
|
import java.lang.reflect.Modifier; |
||||||
|
import @COLLECTIONS@.Collection; |
||||||
|
import @COLLECTIONS@.Iterator; |
||||||
|
|
||||||
|
public class ModifierMatcher implements IdentifierMatcher, Cloneable { |
||||||
|
static final int PUBLIC = Modifier.PUBLIC; |
||||||
|
static final int PROTECTED = Modifier.PROTECTED; |
||||||
|
static final int PRIVATE = Modifier.PRIVATE; |
||||||
|
|
||||||
|
int[] andMasks; |
||||||
|
int[] xorMasks; |
||||||
|
|
||||||
|
public static ModifierMatcher denyAll = new ModifierMatcher(new int[0], |
||||||
|
new int[0]); |
||||||
|
public static ModifierMatcher allowAll = new ModifierMatcher(0, 0); |
||||||
|
|
||||||
|
/* Invariants: |
||||||
|
* \forall i: ~andMasks[i] & xorMasks[i] == 0 |
||||||
|
* \forall i: entries wo. i does not imply entry nr. i |
||||||
|
*/ |
||||||
|
|
||||||
|
public ModifierMatcher() { |
||||||
|
this(0,0); |
||||||
|
} |
||||||
|
|
||||||
|
private ModifierMatcher(int[] ands, int[] xors) { |
||||||
|
andMasks = ands; |
||||||
|
xorMasks = xors; |
||||||
|
} |
||||||
|
|
||||||
|
public ModifierMatcher(int and, int xor) { |
||||||
|
andMasks = new int[] { and }; |
||||||
|
xorMasks = new int[] { xor }; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOption(String option, Collection values) { |
||||||
|
ModifierMatcher mm = this; |
||||||
|
if (option.equals("access")) { |
||||||
|
for(Iterator i = values.iterator(); i.hasNext();) { |
||||||
|
String str = (String) i.next(); |
||||||
|
boolean less = str.charAt(0) == '<'; |
||||||
|
boolean greater = str.charAt(0) == '>'; |
||||||
|
if (less || greater) |
||||||
|
str = str.substring(1); |
||||||
|
str = str.toUpperCase(); |
||||||
|
if (less) { |
||||||
|
int access = |
||||||
|
str.equals("PROTECTED") ? PUBLIC |
||||||
|
: str.equals("PACKAGE") ? PROTECTED |
||||||
|
: str.equals("PRIVATE") ? 0 |
||||||
|
: -1; |
||||||
|
if (access == -1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Unknown access modifier " + str); |
||||||
|
|
||||||
|
mm = mm.forbidAccess(access, true); |
||||||
|
} else { |
||||||
|
int access = |
||||||
|
str.equals("PUBLIC") ? PUBLIC |
||||||
|
: str.equals("PROTECTED") ? PROTECTED |
||||||
|
: str.equals("PACKAGE") ? 0 |
||||||
|
: str.equals("PRIVATE") ? PRIVATE |
||||||
|
: -1; |
||||||
|
if (access == -1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Unknown access " + str); |
||||||
|
mm = mm.forceAccess(access, greater); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (option.equals("modifier")) { |
||||||
|
for(Iterator i = values.iterator(); i.hasNext();) { |
||||||
|
String str = (String) i.next(); |
||||||
|
boolean negate = str.charAt(0) == '!'; |
||||||
|
if (negate) |
||||||
|
str = str.substring(1); |
||||||
|
str = str.toUpperCase(); |
||||||
|
|
||||||
|
int modif = |
||||||
|
str.equals("ABSTRACT") ? Modifier.ABSTRACT |
||||||
|
: str.equals("FINAL") ? Modifier.FINAL |
||||||
|
: str.equals("INTERFACE") ? Modifier.INTERFACE |
||||||
|
: str.equals("NATIVE") ? Modifier.NATIVE |
||||||
|
: str.equals("STATIC") ? Modifier.STATIC |
||||||
|
///#ifdef JDK12
|
||||||
|
/// : str.equals("STRICT") ? Modifier.STRICT
|
||||||
|
///#endif
|
||||||
|
: str.equals("SYNCHRONIZED") ? Modifier.SYNCHRONIZED |
||||||
|
: str.equals("TRANSIENT") ? Modifier.TRANSIENT |
||||||
|
: str.equals("VOLATILE") ? Modifier.VOLATILE |
||||||
|
: -1; |
||||||
|
if (modif == -1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Unknown modifier " + str); |
||||||
|
if (negate) |
||||||
|
mm = mm.forbidModifier(modif); |
||||||
|
else |
||||||
|
mm = mm.forceModifier(modif); |
||||||
|
} |
||||||
|
} else |
||||||
|
throw new IllegalArgumentException("Invalid option `"+option+"'."); |
||||||
|
andMasks = mm.andMasks; |
||||||
|
xorMasks = mm.xorMasks; |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean implies(int and1, int xor1, int and2, int xor2) { |
||||||
|
return ((and1 & and2) == and2 && (xor1 & and2) == xor2); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean implies(int and, int xor) { |
||||||
|
for (int i=0; i < andMasks.length; i++) { |
||||||
|
if (!implies(andMasks[i], xorMasks[i], and, xor)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean impliedBy(int and, int xor) { |
||||||
|
for (int i=0; i< andMasks.length; i++) { |
||||||
|
if (implies(and, xor, andMasks[i], xorMasks[i])) |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean implies(ModifierMatcher mm) { |
||||||
|
for (int i=0; i < andMasks.length; i++) { |
||||||
|
if (!mm.impliedBy(andMasks[i], xorMasks[i])) |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public ModifierMatcher and(ModifierMatcher mm) { |
||||||
|
if (implies(mm)) |
||||||
|
return this; |
||||||
|
if (mm.implies(this)) |
||||||
|
return mm; |
||||||
|
|
||||||
|
ModifierMatcher result = denyAll; |
||||||
|
for (int i=0; i< andMasks.length; i++) |
||||||
|
result = result.or(mm.and(andMasks[i], xorMasks[i])); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public ModifierMatcher or(ModifierMatcher mm) { |
||||||
|
if (implies(mm)) |
||||||
|
return mm; |
||||||
|
if (mm.implies(this)) |
||||||
|
return this; |
||||||
|
ModifierMatcher result = this; |
||||||
|
for (int i=0; i < mm.andMasks.length; i++) |
||||||
|
result = result.or(mm.andMasks[i], mm.xorMasks[i]); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
private ModifierMatcher and(int and, int xor) { |
||||||
|
if (this.implies(and, xor)) |
||||||
|
return this; |
||||||
|
int newCount = 0; |
||||||
|
next_i: |
||||||
|
for (int i=0; i < andMasks.length; i++) { |
||||||
|
if (implies(and, xor, andMasks[i], xorMasks[i])) |
||||||
|
continue next_i; |
||||||
|
|
||||||
|
for (int j=0; j < andMasks.length; j++) { |
||||||
|
if (j != i |
||||||
|
&& implies(and | andMasks[j], xor | xorMasks[j], |
||||||
|
andMasks[i], xorMasks[i])) |
||||||
|
continue next_i; |
||||||
|
} |
||||||
|
newCount++; |
||||||
|
} |
||||||
|
if (newCount == 0) |
||||||
|
return new ModifierMatcher(and, xor); |
||||||
|
int[] ands = new int[newCount]; |
||||||
|
int[] xors = new int[newCount]; |
||||||
|
int index = 0; |
||||||
|
next_i: |
||||||
|
for (int i=0; i < newCount; i++) { |
||||||
|
if (implies(and, xor, andMasks[i], xorMasks[i])) |
||||||
|
continue next_i; |
||||||
|
|
||||||
|
for (int j=0; j < andMasks.length; j++) { |
||||||
|
if (j != i |
||||||
|
&& implies(and | andMasks[j], xor | xorMasks[j], |
||||||
|
andMasks[i], xorMasks[i])) |
||||||
|
continue next_i; |
||||||
|
} |
||||||
|
|
||||||
|
ands[index] = andMasks[i] | and; |
||||||
|
xors[index] = xorMasks[i] | xor; |
||||||
|
index++; |
||||||
|
} |
||||||
|
return new ModifierMatcher(ands, xors); |
||||||
|
} |
||||||
|
|
||||||
|
private ModifierMatcher or(int and, int xor) { |
||||||
|
int matchIndex = -1; |
||||||
|
if (this == denyAll) |
||||||
|
return new ModifierMatcher(and, xor); |
||||||
|
for (int i=0; i< andMasks.length; i++) { |
||||||
|
if (implies(and, xor, andMasks[i], xorMasks[i])) |
||||||
|
return this; |
||||||
|
if (implies(andMasks[i], xorMasks[i], and, xor)) { |
||||||
|
matchIndex = i; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
int[] ands, xors; |
||||||
|
if (matchIndex == -1) { |
||||||
|
matchIndex = andMasks.length; |
||||||
|
ands = new int[matchIndex+1]; |
||||||
|
xors = new int[matchIndex+1]; |
||||||
|
System.arraycopy(andMasks, 0, ands, 0, matchIndex); |
||||||
|
System.arraycopy(xorMasks, 0, xors, 0, matchIndex); |
||||||
|
} else { |
||||||
|
ands = (int[]) andMasks.clone(); |
||||||
|
xors = (int[]) xorMasks.clone(); |
||||||
|
} |
||||||
|
ands[matchIndex] = and; |
||||||
|
xors[matchIndex] = xor; |
||||||
|
return new ModifierMatcher(ands, xors); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new ModifierMatcher, that matches only modifiers, we |
||||||
|
* also match and also forces the access rights, to be accessModif |
||||||
|
* (or less restrictive). |
||||||
|
* @param accessModif the access modifier. Use 0 for package access, |
||||||
|
* or Modifier.PRIVATE/PROTECTED/PUBLIC. |
||||||
|
* @param andAbove allow to be less restrictive. |
||||||
|
* @return a new modifier matcher that will also use the given accesses. |
||||||
|
*/ |
||||||
|
public ModifierMatcher forceAccess(int accessModif, boolean andAbove) { |
||||||
|
if (andAbove) { |
||||||
|
if (accessModif == Modifier.PRIVATE) |
||||||
|
return this; |
||||||
|
if (accessModif == 0) |
||||||
|
return this.and(Modifier.PRIVATE, 0); |
||||||
|
|
||||||
|
ModifierMatcher result = this.and(Modifier.PUBLIC, PUBLIC); |
||||||
|
if (accessModif == Modifier.PROTECTED) |
||||||
|
return result |
||||||
|
.or(this.and(Modifier.PROTECTED, Modifier.PROTECTED)); |
||||||
|
if (accessModif == Modifier.PUBLIC) |
||||||
|
return result; |
||||||
|
throw new IllegalArgumentException(""+accessModif); |
||||||
|
} else { |
||||||
|
if (accessModif == 0) |
||||||
|
return this.and(Modifier.PRIVATE | |
||||||
|
Modifier.PROTECTED | Modifier.PUBLIC, 0); |
||||||
|
else |
||||||
|
return this.and(accessModif, accessModif); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public ModifierMatcher forbidAccess(int accessModif, boolean andAbove) { |
||||||
|
if (andAbove) { |
||||||
|
if (accessModif == Modifier.PRIVATE) |
||||||
|
// This forbids all access.
|
||||||
|
return denyAll; |
||||||
|
if (accessModif == 0) |
||||||
|
return this.and(Modifier.PRIVATE, Modifier.PRIVATE); |
||||||
|
if (accessModif == Modifier.PROTECTED) |
||||||
|
return this.and(Modifier.PROTECTED | Modifier.PUBLIC, 0); |
||||||
|
if (accessModif == Modifier.PUBLIC) |
||||||
|
return this.and(Modifier.PUBLIC, 0); |
||||||
|
throw new IllegalArgumentException(""+accessModif); |
||||||
|
} else { |
||||||
|
if (accessModif == 0) { |
||||||
|
return this.and(Modifier.PRIVATE, Modifier.PRIVATE) |
||||||
|
.or(this.and(Modifier.PROTECTED, Modifier.PROTECTED)) |
||||||
|
.or(this.and(Modifier.PUBLIC, Modifier.PUBLIC)); |
||||||
|
} else |
||||||
|
return this.and(accessModif, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public final ModifierMatcher forceModifier(int modifier) { |
||||||
|
return this.and(modifier, modifier); |
||||||
|
} |
||||||
|
|
||||||
|
public final ModifierMatcher forbidModifier(int modifier) { |
||||||
|
return this.and(modifier, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean matches(int modifiers) { |
||||||
|
for (int i=0; i< andMasks.length; i++) |
||||||
|
if ((modifiers & andMasks[i]) == xorMasks[i]) |
||||||
|
return true; |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean matches(Identifier ident) { |
||||||
|
int modifiers; |
||||||
|
/* XXX NEW INTERFACE OR ANOTHER METHOD IN IDENTIFIER? */ |
||||||
|
if (ident instanceof ClassIdentifier) |
||||||
|
modifiers = ((ClassIdentifier) ident).getModifiers(); |
||||||
|
else if (ident instanceof MethodIdentifier) |
||||||
|
modifiers = ((MethodIdentifier) ident).getModifiers(); |
||||||
|
else if (ident instanceof FieldIdentifier) |
||||||
|
modifiers = ((FieldIdentifier) ident).getModifiers(); |
||||||
|
else |
||||||
|
return false; |
||||||
|
return matches(modifiers); |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean matchesSub(Identifier ident, String name) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public final String getNextComponent(Identifier ident) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public Object clone() { |
||||||
|
try { |
||||||
|
return super.clone(); |
||||||
|
} catch (CloneNotSupportedException ex) { |
||||||
|
throw new IncompatibleClassChangeError(getClass().getName()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,110 @@ |
|||||||
|
/* MultiIdentifierMatcher 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.modules; |
||||||
|
import jode.obfuscator.*; |
||||||
|
import @COLLECTIONS@.Collection; |
||||||
|
|
||||||
|
public class MultiIdentifierMatcher implements IdentifierMatcher, OptionHandler { |
||||||
|
/** |
||||||
|
* Useful constant for giving to the constructor. |
||||||
|
*/ |
||||||
|
public static boolean OR = true; |
||||||
|
/** |
||||||
|
* Useful constant for giving to the constructor. |
||||||
|
*/ |
||||||
|
public static boolean AND = false; |
||||||
|
|
||||||
|
IdentifierMatcher[] matchers; |
||||||
|
boolean isOr; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an empty MultiIdentifierMatcher. |
||||||
|
*/ |
||||||
|
public MultiIdentifierMatcher() { |
||||||
|
this.matchers = new IdentifierMatcher[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an IdentifierMatcher out of other matchers. |
||||||
|
* @param isOr if true, match should return the logical (shortcut) |
||||||
|
* or of the underlying matchers, if false it returns the logical and. |
||||||
|
* @param matchers the underlying matchers |
||||||
|
*/ |
||||||
|
public MultiIdentifierMatcher(boolean isOr, |
||||||
|
IdentifierMatcher[] matchers) { |
||||||
|
this.isOr = isOr; |
||||||
|
this.matchers = matchers; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOption(String option, Collection values) { |
||||||
|
if (option.equals("or")) { |
||||||
|
isOr = true; |
||||||
|
matchers = (IdentifierMatcher[]) |
||||||
|
values.toArray(new IdentifierMatcher[values.size()]); |
||||||
|
} else if (option.equals("and")) { |
||||||
|
isOr = false; |
||||||
|
matchers = (IdentifierMatcher[]) |
||||||
|
values.toArray(new IdentifierMatcher[values.size()]); |
||||||
|
} else |
||||||
|
throw new IllegalArgumentException("Invalid option `"+option+"'."); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public boolean matches(Identifier ident) { |
||||||
|
for (int i=0; i< matchers.length; i++) { |
||||||
|
if (matchers[i].matches(ident) == isOr) |
||||||
|
return isOr; |
||||||
|
} |
||||||
|
return !isOr; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean matchesSub(Identifier ident, String name) { |
||||||
|
for (int i=0; i< matchers.length; i++) { |
||||||
|
if (matchers[i].matchesSub(ident, name) == isOr) |
||||||
|
return isOr; |
||||||
|
} |
||||||
|
return !isOr; |
||||||
|
} |
||||||
|
|
||||||
|
public String getNextComponent(Identifier ident) { |
||||||
|
if (isOr == AND) { |
||||||
|
for (int i=0; i< matchers.length; i++) { |
||||||
|
String next = matchers[i].getNextComponent(ident); |
||||||
|
if (next != null && matchesSub(ident, next)) |
||||||
|
return next; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
// OR case
|
||||||
|
String next = null; |
||||||
|
for (int i = 0; i < matchers.length; i++) { |
||||||
|
if (!matchesSub(ident, null)) |
||||||
|
continue; |
||||||
|
if (next != null |
||||||
|
&& matchers[i].getNextComponent(ident) != next) |
||||||
|
return null; |
||||||
|
next = matchers[i].getNextComponent(ident); |
||||||
|
if (next == null) |
||||||
|
return null; |
||||||
|
} |
||||||
|
return next; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,99 @@ |
|||||||
|
/* NameSwapper 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.modules; |
||||||
|
import jode.obfuscator.*; |
||||||
|
|
||||||
|
import @COLLECTIONS@.Collection; |
||||||
|
import @COLLECTIONS@.Set; |
||||||
|
import @COLLECTIONS@.HashSet; |
||||||
|
import @COLLECTIONS@.Iterator; |
||||||
|
import @COLLECTIONS@.Random; |
||||||
|
import @COLLECTIONEXTRA@.UnsupportedOperationException; |
||||||
|
|
||||||
|
|
||||||
|
public class NameSwapper implements Renamer { |
||||||
|
private Random rand; |
||||||
|
private Set packs, clazzes, methods, fields, locals; |
||||||
|
|
||||||
|
public NameSwapper(boolean swapAll, long seed) { |
||||||
|
if (swapAll) { |
||||||
|
packs = clazzes = methods = fields = locals = new HashSet(); |
||||||
|
} else { |
||||||
|
packs = new HashSet(); |
||||||
|
clazzes = new HashSet(); |
||||||
|
methods = new HashSet(); |
||||||
|
fields = new HashSet(); |
||||||
|
locals = new HashSet(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public NameSwapper(boolean swapAll) { |
||||||
|
this(swapAll, System.currentTimeMillis()); |
||||||
|
} |
||||||
|
|
||||||
|
private class NameGenerator implements Iterator { |
||||||
|
Collection pool; |
||||||
|
|
||||||
|
NameGenerator(Collection c) { |
||||||
|
pool = c; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasNext() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public Object next() { |
||||||
|
int pos = rand.nextInt(pool.size()); |
||||||
|
Iterator i = pool.iterator(); |
||||||
|
while (pos > 0) |
||||||
|
i.next(); |
||||||
|
return (String) i.next(); |
||||||
|
} |
||||||
|
|
||||||
|
public void remove() { |
||||||
|
throw new UnsupportedOperationException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public final Collection getCollection(Identifier ident) { |
||||||
|
if (ident instanceof PackageIdentifier) |
||||||
|
return packs; |
||||||
|
else if (ident instanceof ClassIdentifier) |
||||||
|
return clazzes; |
||||||
|
else if (ident instanceof MethodIdentifier) |
||||||
|
return methods; |
||||||
|
else if (ident instanceof FieldIdentifier) |
||||||
|
return fields; |
||||||
|
else if (ident instanceof LocalIdentifier) |
||||||
|
return locals; |
||||||
|
else |
||||||
|
throw new IllegalArgumentException(ident.getClass().getName()); |
||||||
|
} |
||||||
|
|
||||||
|
public final void addIdentifierName(Identifier ident) { |
||||||
|
getCollection(ident).add(ident.getName()); |
||||||
|
} |
||||||
|
|
||||||
|
public Iterator generateNames(Identifier ident) { |
||||||
|
return new NameGenerator(getCollection(ident)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,308 @@ |
|||||||
|
/* 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.modules; |
||||||
|
import jode.bytecode.*; |
||||||
|
import jode.obfuscator.*; |
||||||
|
import jode.AssertError; |
||||||
|
import jode.GlobalOptions; |
||||||
|
|
||||||
|
import @COLLECTIONS@.ListIterator; |
||||||
|
|
||||||
|
public class RemovePopAnalyzer implements CodeTransformer, Opcodes { |
||||||
|
public RemovePopAnalyzer() { |
||||||
|
} |
||||||
|
|
||||||
|
public void transformCode(BytecodeInfo bytecode) { |
||||||
|
int poppush[] = new int[2]; |
||||||
|
ListIterator iter = bytecode.getInstructions().listIterator(); |
||||||
|
next_pop: |
||||||
|
while (iter.hasNext()) { |
||||||
|
Instruction popInstr = (Instruction) iter.next(); |
||||||
|
boolean isPop2 = false; |
||||||
|
switch (popInstr.getOpcode()) { |
||||||
|
case opc_nop: { |
||||||
|
iter.remove(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
case opc_pop2: |
||||||
|
isPop2 = true; |
||||||
|
case opc_pop: |
||||||
|
if (popInstr.getPreds() != null) |
||||||
|
// Can't handle pop with multiple predecessors
|
||||||
|
continue next_pop; |
||||||
|
Handler[] handlers = bytecode.getExceptionHandlers(); |
||||||
|
for (int i=0; i < handlers.length; i++) |
||||||
|
if (handlers[i].catcher == popInstr) |
||||||
|
continue next_pop; |
||||||
|
|
||||||
|
// remove pop, we will insert it again if something
|
||||||
|
// bad happened.
|
||||||
|
iter.remove(); |
||||||
|
|
||||||
|
// remember position of pop, so we can insert it again.
|
||||||
|
Instruction popPrevious = (Instruction) iter.previous(); |
||||||
|
Instruction instr = popPrevious; |
||||||
|
int count = 0; |
||||||
|
while (true) { |
||||||
|
if (instr.getSuccs() != null |
||||||
|
|| instr.doesAlwaysJump()) { |
||||||
|
instr = null; |
||||||
|
break; |
||||||
|
} |
||||||
|
instr.getStackPopPush(poppush); |
||||||
|
|
||||||
|
if (count < poppush[1]) { |
||||||
|
if (count == 0) |
||||||
|
break; |
||||||
|
|
||||||
|
int opcode = instr.getOpcode(); |
||||||
|
/* If this is a dup and the instruction popped is the |
||||||
|
* duplicated element, remove the dup and the pop |
||||||
|
*/ |
||||||
|
if (count <= 3 && opcode == (opc_dup + count - 1)) { |
||||||
|
iter.remove(); |
||||||
|
if (!isPop2) |
||||||
|
continue next_pop; |
||||||
|
|
||||||
|
// We have to consider a pop instead of a
|
||||||
|
// pop2 now.
|
||||||
|
popInstr = new Instruction(opc_pop); |
||||||
|
isPop2 = false; |
||||||
|
instr = (Instruction) iter.previous(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (isPop2 |
||||||
|
&& count > 1 && count <= 4 |
||||||
|
&& opcode == (opc_dup2 + count-2)) { |
||||||
|
iter.remove(); |
||||||
|
continue next_pop; |
||||||
|
} |
||||||
|
/* Otherwise popping is not possible */ |
||||||
|
instr = null; |
||||||
|
break; |
||||||
|
} |
||||||
|
count += poppush[0] - poppush[1]; |
||||||
|
instr = (Instruction) iter.previous(); |
||||||
|
} |
||||||
|
|
||||||
|
if (instr == null) { |
||||||
|
// We insert the pop at the previous position
|
||||||
|
while (iter.next() != popPrevious) |
||||||
|
{} |
||||||
|
if (!isPop2 && popPrevious.getOpcode() == opc_pop) { |
||||||
|
// merge pop with popPrevious
|
||||||
|
iter.set(new Instruction(opc_pop2)); |
||||||
|
} else |
||||||
|
iter.add(popInstr); |
||||||
|
continue; |
||||||
|
} |
||||||
|
int opcode = instr.getOpcode(); |
||||||
|
switch (opcode) { |
||||||
|
case opc_ldc2_w: |
||||||
|
case opc_lload: case opc_dload: |
||||||
|
if (!isPop2) |
||||||
|
throw new AssertError("pop on long"); |
||||||
|
iter.remove(); |
||||||
|
continue; |
||||||
|
case opc_ldc: |
||||||
|
case opc_iload: case opc_fload: case opc_aload: |
||||||
|
case opc_dup: |
||||||
|
case opc_new: |
||||||
|
if (isPop2) |
||||||
|
iter.set(new Instruction(opc_pop)); |
||||||
|
else |
||||||
|
iter.remove(); |
||||||
|
continue; |
||||||
|
case opc_iaload: case opc_faload: case opc_aaload: |
||||||
|
case opc_baload: case opc_caload: case opc_saload: |
||||||
|
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: |
||||||
|
/* We have to pop one entry more. */ |
||||||
|
iter.next(); |
||||||
|
iter.add(popInstr); |
||||||
|
iter.previous(); |
||||||
|
iter.previous(); |
||||||
|
iter.set(new Instruction(opc_pop)); |
||||||
|
continue; |
||||||
|
|
||||||
|
case opc_dup_x1: |
||||||
|
iter.set(new Instruction(opc_swap)); |
||||||
|
iter.next(); |
||||||
|
if (isPop2) |
||||||
|
iter.add(new Instruction(opc_pop)); |
||||||
|
continue; |
||||||
|
|
||||||
|
case opc_dup2: |
||||||
|
if (isPop2) { |
||||||
|
iter.remove(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
break; |
||||||
|
case opc_swap: |
||||||
|
if (isPop2) { |
||||||
|
iter.set(popInstr); |
||||||
|
continue; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_lneg: case opc_dneg: |
||||||
|
case opc_l2d: case opc_d2l: |
||||||
|
case opc_laload: case opc_daload: |
||||||
|
if (!isPop2) |
||||||
|
throw new AssertError("pop on long"); |
||||||
|
/* 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: |
||||||
|
iter.set(popInstr); |
||||||
|
continue; |
||||||
|
|
||||||
|
case opc_l2i: case opc_l2f: |
||||||
|
case opc_d2i: case opc_d2f: |
||||||
|
if (isPop2) { |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(opc_pop)); |
||||||
|
iter.previous(); |
||||||
|
iter.previous(); |
||||||
|
} |
||||||
|
iter.set(new Instruction(opc_pop2)); |
||||||
|
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 (!isPop2) |
||||||
|
throw new AssertError("pop on long"); |
||||||
|
iter.next(); |
||||||
|
iter.add(popInstr); |
||||||
|
iter.previous(); |
||||||
|
iter.previous(); |
||||||
|
iter.set(new Instruction(opc_pop2)); |
||||||
|
continue; |
||||||
|
case opc_lshl: case opc_lshr: case opc_lushr: |
||||||
|
if (!isPop2) |
||||||
|
throw new AssertError("pop on long"); |
||||||
|
iter.next(); |
||||||
|
iter.add(popInstr); |
||||||
|
iter.previous(); |
||||||
|
iter.previous(); |
||||||
|
iter.set(new Instruction(opc_pop)); |
||||||
|
continue; |
||||||
|
|
||||||
|
case opc_i2l: case opc_i2d: |
||||||
|
case opc_f2l: case opc_f2d: |
||||||
|
if (!isPop2) |
||||||
|
throw new AssertError("pop on long"); |
||||||
|
iter.set(new Instruction(opc_pop)); |
||||||
|
continue; |
||||||
|
|
||||||
|
case opc_lcmp: |
||||||
|
case opc_dcmpl: case opc_dcmpg: |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(opc_pop2)); |
||||||
|
if (isPop2) { |
||||||
|
iter.add(new Instruction(opc_pop)); |
||||||
|
iter.previous(); |
||||||
|
} |
||||||
|
iter.previous(); |
||||||
|
iter.previous(); |
||||||
|
iter.set(new Instruction(opc_pop2)); |
||||||
|
continue; |
||||||
|
|
||||||
|
case opc_getstatic: |
||||||
|
case opc_getfield: { |
||||||
|
Reference ref = instr.getReference(); |
||||||
|
int size = TypeSignature.getTypeSize(ref.getType()); |
||||||
|
if (size == 2 && !isPop2) |
||||||
|
throw new AssertError("pop on long"); |
||||||
|
if (opcode == opc_getfield) |
||||||
|
size--; |
||||||
|
switch (size) { |
||||||
|
case 0: |
||||||
|
iter.set(popInstr); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
if (isPop2) { |
||||||
|
iter.set(new Instruction(opc_pop)); |
||||||
|
break; |
||||||
|
} |
||||||
|
/* fall through */ |
||||||
|
case 2: |
||||||
|
iter.remove(); |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
case opc_multianewarray: { |
||||||
|
int dims = instr.getDimensions(); |
||||||
|
if (--dims > 0) { |
||||||
|
iter.next(); |
||||||
|
while (dims-- > 0) { |
||||||
|
iter.add(new Instruction(opc_pop)); |
||||||
|
iter.previous(); |
||||||
|
} |
||||||
|
iter.previous(); |
||||||
|
} |
||||||
|
iter.set(popInstr); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
case opc_invokevirtual: |
||||||
|
case opc_invokespecial: |
||||||
|
case opc_invokestatic: |
||||||
|
case opc_invokeinterface: |
||||||
|
if (TypeSignature.getReturnSize |
||||||
|
(instr.getReference().getType()) != 1) |
||||||
|
break; |
||||||
|
/* fall through */ |
||||||
|
case opc_checkcast: |
||||||
|
if (isPop2) { |
||||||
|
/* This is/may be a double pop on a single value |
||||||
|
* split it and continue with second half |
||||||
|
*/ |
||||||
|
iter.next(); |
||||||
|
iter.add(new Instruction(opc_pop)); |
||||||
|
iter.add(new Instruction(opc_pop)); |
||||||
|
iter.previous(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
// append the pop behind the unresolvable opcode.
|
||||||
|
iter.next(); |
||||||
|
iter.add(popInstr); |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,184 @@ |
|||||||
|
/* 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.modules; |
||||||
|
import jode.bytecode.Handler; |
||||||
|
import jode.bytecode.Opcodes; |
||||||
|
import jode.bytecode.ClassInfo; |
||||||
|
import jode.bytecode.BytecodeInfo; |
||||||
|
import jode.bytecode.Instruction; |
||||||
|
import jode.bytecode.Reference; |
||||||
|
import jode.bytecode.TypeSignature; |
||||||
|
import jode.obfuscator.Identifier; |
||||||
|
import jode.obfuscator.*; |
||||||
|
import jode.GlobalOptions; |
||||||
|
|
||||||
|
import @COLLECTIONS@.Iterator; |
||||||
|
import @COLLECTIONS@.ListIterator; |
||||||
|
|
||||||
|
public class SimpleAnalyzer implements CodeAnalyzer, Opcodes { |
||||||
|
|
||||||
|
public Identifier canonizeReference(Instruction instr) { |
||||||
|
Reference ref = instr.getReference(); |
||||||
|
Identifier ident = Main.getClassBundle().getIdentifier(ref); |
||||||
|
String clName = ref.getClazz(); |
||||||
|
String realClazzName; |
||||||
|
if (ident != null) { |
||||||
|
ClassIdentifier clazz = (ClassIdentifier)ident.getParent(); |
||||||
|
realClazzName = "L" + (clazz.getFullName() |
||||||
|
.replace('.', '/')) + ";"; |
||||||
|
} else { |
||||||
|
/* We have to look at the ClassInfo's instead, to |
||||||
|
* point to the right method. |
||||||
|
*/ |
||||||
|
ClassInfo clazz; |
||||||
|
if (clName.charAt(0) == '[') { |
||||||
|
/* Arrays don't define new methods (well clone(), |
||||||
|
* but that can be ignored). |
||||||
|
*/ |
||||||
|
clazz = ClassInfo.javaLangObject; |
||||||
|
} else { |
||||||
|
clazz = ClassInfo.forName |
||||||
|
(clName.substring(1, clName.length()-1) |
||||||
|
.replace('/','.')); |
||||||
|
} |
||||||
|
if (instr.getOpcode() >= opc_invokevirtual) { |
||||||
|
while (clazz != null |
||||||
|
&& clazz.findMethod(ref.getName(), |
||||||
|
ref.getType()) == null) |
||||||
|
clazz = clazz.getSuperclass(); |
||||||
|
} else { |
||||||
|
while (clazz != null |
||||||
|
&& clazz.findField(ref.getName(), |
||||||
|
ref.getType()) == null) |
||||||
|
clazz = clazz.getSuperclass(); |
||||||
|
} |
||||||
|
|
||||||
|
if (clazz == null) { |
||||||
|
GlobalOptions.err.println("WARNING: Can't find reference: " |
||||||
|
+ref); |
||||||
|
realClazzName = clName; |
||||||
|
} else |
||||||
|
realClazzName = "L" + clazz.getName().replace('.', '/') + ";"; |
||||||
|
} |
||||||
|
if (!realClazzName.equals(ref.getClazz())) { |
||||||
|
ref = Reference.getReference(realClazzName, |
||||||
|
ref.getName(), ref.getType()); |
||||||
|
instr.setReference(ref); |
||||||
|
} |
||||||
|
return ident; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Reads the opcodes out of the code info and determine its |
||||||
|
* references |
||||||
|
* @return an enumeration of the references. |
||||||
|
*/ |
||||||
|
public void analyzeCode(MethodIdentifier m, BytecodeInfo bytecode) { |
||||||
|
for (Iterator iter = bytecode.getInstructions().iterator(); |
||||||
|
iter.hasNext(); ) { |
||||||
|
Instruction instr = (Instruction) iter.next(); |
||||||
|
switch (instr.getOpcode()) { |
||||||
|
case opc_checkcast: |
||||||
|
case opc_instanceof: |
||||||
|
case opc_multianewarray: { |
||||||
|
String clName = instr.getClazzType(); |
||||||
|
int i = 0; |
||||||
|
while (i < clName.length() && clName.charAt(i) == '[') |
||||||
|
i++; |
||||||
|
if (i < clName.length() && clName.charAt(i) == 'L') { |
||||||
|
clName = clName.substring(i+1, clName.length()-1) |
||||||
|
.replace('/','.'); |
||||||
|
Main.getClassBundle().reachableClass(clName); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case opc_invokespecial: |
||||||
|
case opc_invokestatic: |
||||||
|
case opc_invokeinterface: |
||||||
|
case opc_invokevirtual: |
||||||
|
case opc_putstatic: |
||||||
|
case opc_putfield: |
||||||
|
m.setGlobalSideEffects(); |
||||||
|
/* fall through */ |
||||||
|
case opc_getstatic: |
||||||
|
case opc_getfield: { |
||||||
|
Identifier ident = canonizeReference(instr); |
||||||
|
if (ident != null) { |
||||||
|
if (instr.getOpcode() == opc_putstatic |
||||||
|
|| instr.getOpcode() == opc_putfield) { |
||||||
|
FieldIdentifier fi = (FieldIdentifier) ident; |
||||||
|
if (fi != null && !fi.isNotConstant()) |
||||||
|
fi.setNotConstant(); |
||||||
|
} else if (instr.getOpcode() == opc_invokevirtual |
||||||
|
|| instr.getOpcode() == opc_invokeinterface) { |
||||||
|
((ClassIdentifier) ident.getParent()) |
||||||
|
.reachableReference(instr.getReference(), true); |
||||||
|
} else { |
||||||
|
ident.setReachable(); |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Handler[] handlers = bytecode.getExceptionHandlers(); |
||||||
|
for (int i=0; i< handlers.length; i++) { |
||||||
|
if (handlers[i].type != null) |
||||||
|
Main.getClassBundle() |
||||||
|
.reachableClass(handlers[i].type); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void transformCode(BytecodeInfo bytecode) { |
||||||
|
for (ListIterator iter = bytecode.getInstructions().listIterator(); |
||||||
|
iter.hasNext(); ) { |
||||||
|
Instruction instr = (Instruction) iter.next(); |
||||||
|
if (instr.getOpcode() == opc_putstatic |
||||||
|
|| instr.getOpcode() == opc_putfield) { |
||||||
|
Reference ref = instr.getReference(); |
||||||
|
FieldIdentifier fi = (FieldIdentifier) |
||||||
|
Main.getClassBundle().getIdentifier(ref); |
||||||
|
if (fi != null |
||||||
|
&& (Main.stripping & Main.STRIP_UNREACH) != 0 |
||||||
|
&& !fi.isReachable()) { |
||||||
|
/* Replace instruction with pop opcodes. */ |
||||||
|
int stacksize = |
||||||
|
(instr.getOpcode() |
||||||
|
== Instruction.opc_putstatic) ? 0 : 1; |
||||||
|
stacksize += TypeSignature.getTypeSize(ref.getType()); |
||||||
|
switch (stacksize) { |
||||||
|
case 1: |
||||||
|
iter.set(new Instruction(Instruction.opc_pop)); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
iter.set(new Instruction(Instruction.opc_pop2)); |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
iter.set(new Instruction(Instruction.opc_pop2)); |
||||||
|
iter.add(new Instruction(Instruction.opc_pop)); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
/* StrongRenamer 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.modules; |
||||||
|
import jode.obfuscator.*; |
||||||
|
import @COLLECTIONS@.Collection; |
||||||
|
import @COLLECTIONS@.Iterator; |
||||||
|
import @COLLECTIONEXTRA@.UnsupportedOperationException; |
||||||
|
|
||||||
|
public class StrongRenamer implements Renamer, OptionHandler { |
||||||
|
static String[] idents = { |
||||||
|
"Package", "Class", "Field", "Method", "Local" |
||||||
|
}; |
||||||
|
static String[] parts = { |
||||||
|
"Start", "Part" |
||||||
|
}; |
||||||
|
String charsets[][]; |
||||||
|
|
||||||
|
public StrongRenamer() { |
||||||
|
charsets = new String[idents.length][parts.length]; |
||||||
|
for (int i=0; i< idents.length; i++) |
||||||
|
for (int j=0; j< parts.length; j++) |
||||||
|
charsets[i][j] = "abcdefghijklmnopqrstuvwxyz"; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOption(String option, Collection values) { |
||||||
|
if (option.startsWith("charset")) { |
||||||
|
Object value = values.iterator().next(); |
||||||
|
if (values.size() != 1 || !(value instanceof String)) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Only string parameter are supported."); |
||||||
|
String set = (String) value; |
||||||
|
String remOpt = option.substring("charset".length()); |
||||||
|
int part = -1, ident = -1; |
||||||
|
if (remOpt.length() > 0) { |
||||||
|
for (int i=0; i < idents.length; i++) { |
||||||
|
if (remOpt.startsWith(idents[i])) { |
||||||
|
remOpt = remOpt.substring(idents[i].length()); |
||||||
|
ident = i; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (remOpt.length() > 0) { |
||||||
|
for (int j=0; j < parts.length; j++) { |
||||||
|
if (remOpt.startsWith(parts[j])) { |
||||||
|
remOpt = remOpt.substring(parts[j].length()); |
||||||
|
part = j; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (remOpt.length() > 0) |
||||||
|
throw new IllegalArgumentException("Invalid charset `" |
||||||
|
+option+"'"); |
||||||
|
for (int i = 0; i < idents.length; i++) { |
||||||
|
if (ident >= 0 && ident != i) |
||||||
|
continue; |
||||||
|
for (int j = 0; j < parts.length; j++) { |
||||||
|
if (part >= 0 && part != j) |
||||||
|
continue; |
||||||
|
charsets[i][j] = set; |
||||||
|
} |
||||||
|
} |
||||||
|
} else |
||||||
|
throw new IllegalArgumentException("Invalid option `" |
||||||
|
+option+"'"); |
||||||
|
} |
||||||
|
|
||||||
|
public Iterator generateNames(Identifier ident) { |
||||||
|
final String[] currCharset; |
||||||
|
if (ident instanceof PackageIdentifier) |
||||||
|
currCharset = charsets[0]; |
||||||
|
else if (ident instanceof PackageIdentifier) |
||||||
|
currCharset = charsets[1]; |
||||||
|
else if (ident instanceof ClassIdentifier) |
||||||
|
currCharset = charsets[2]; |
||||||
|
else if (ident instanceof FieldIdentifier) |
||||||
|
currCharset = charsets[3]; |
||||||
|
else if (ident instanceof MethodIdentifier) |
||||||
|
currCharset = charsets[4]; |
||||||
|
else if (ident instanceof LocalIdentifier) |
||||||
|
currCharset = charsets[5]; |
||||||
|
else |
||||||
|
throw new IllegalArgumentException(ident.getClass().getName()); |
||||||
|
|
||||||
|
return new Iterator() { |
||||||
|
char[] name = null; |
||||||
|
|
||||||
|
public boolean hasNext() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
public Object next() { |
||||||
|
if (name == null) { |
||||||
|
name = new char[] { currCharset[0].charAt(0) }; |
||||||
|
return new String(name); |
||||||
|
} |
||||||
|
|
||||||
|
int pos = name.length - 1; |
||||||
|
String charset = currCharset[1]; |
||||||
|
while (pos >= 0) { |
||||||
|
if (pos == 0) |
||||||
|
charset = currCharset[0]; |
||||||
|
|
||||||
|
int index = charset.indexOf(name[pos]) + 1; |
||||||
|
if (index < charset.length()) { |
||||||
|
name[pos] = charset.charAt(index); |
||||||
|
return new String(name); |
||||||
|
} |
||||||
|
name[pos--] = charset.charAt(0); |
||||||
|
} |
||||||
|
|
||||||
|
name = new char[name.length+1]; |
||||||
|
name[0] = currCharset[0].charAt(0); |
||||||
|
char firstCont = currCharset[1].charAt(0); |
||||||
|
for (int i=1; i <name.length; i++) |
||||||
|
name[i] = firstCont; |
||||||
|
return new String(name); |
||||||
|
} |
||||||
|
|
||||||
|
public void remove() { |
||||||
|
throw new UnsupportedOperationException(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
/* UniqueRenamer 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.modules; |
||||||
|
import jode.obfuscator.*; |
||||||
|
|
||||||
|
import @COLLECTIONS@.Iterator; |
||||||
|
import @COLLECTIONEXTRA@.UnsupportedOperationException; |
||||||
|
|
||||||
|
public class UniqueRenamer implements Renamer { |
||||||
|
static int serialnr = 0; |
||||||
|
public Iterator generateNames(Identifier ident) { |
||||||
|
return new Iterator() { |
||||||
|
public boolean hasNext() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public Object next() { |
||||||
|
return "xxx" + serialnr++; |
||||||
|
} |
||||||
|
public void remove() { |
||||||
|
throw new UnsupportedOperationException(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
/* 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.modules; |
||||||
|
import jode.obfuscator.*; |
||||||
|
import @COLLECTIONS@.Collection; |
||||||
|
|
||||||
|
public class WildCard implements IdentifierMatcher, OptionHandler { |
||||||
|
|
||||||
|
String wildcard; |
||||||
|
int firstStar; |
||||||
|
|
||||||
|
public WildCard() { |
||||||
|
} |
||||||
|
|
||||||
|
public WildCard(String wild) { |
||||||
|
wildcard = wild; |
||||||
|
firstStar = wildcard.indexOf('*'); |
||||||
|
} |
||||||
|
|
||||||
|
public void setOption(String option, Collection values) { |
||||||
|
if (option.equals("value")) { |
||||||
|
if (values.size() != 1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Wildcard supports only one value."); |
||||||
|
wildcard = (String) values.iterator().next(); |
||||||
|
firstStar = wildcard.indexOf('*'); |
||||||
|
} else |
||||||
|
throw new IllegalArgumentException("Invalid option `"+option+"'."); |
||||||
|
} |
||||||
|
|
||||||
|
public String getNextComponent(Identifier ident) { |
||||||
|
String prefix = ident.getFullName(); |
||||||
|
if (prefix.length() > 0) |
||||||
|
prefix += "."; |
||||||
|
|
||||||
|
int lastDot = prefix.length(); |
||||||
|
if (!wildcard.startsWith(prefix)) |
||||||
|
return null; |
||||||
|
|
||||||
|
int nextDot = wildcard.indexOf('.', lastDot); |
||||||
|
if (nextDot > 0 |
||||||
|
&& (nextDot <= firstStar || firstStar == -1)) |
||||||
|
return wildcard.substring(lastDot, nextDot); |
||||||
|
else if (firstStar == -1) |
||||||
|
return wildcard.substring(lastDot); |
||||||
|
else |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean matchesSub(Identifier ident, String subident) { |
||||||
|
String prefix = ident.getFullName(); |
||||||
|
if (prefix.length() > 0) |
||||||
|
prefix += "."; |
||||||
|
if (subident != null) |
||||||
|
prefix += subident; |
||||||
|
if (firstStar == -1 || firstStar >= prefix.length()) |
||||||
|
return wildcard.startsWith(prefix); |
||||||
|
return prefix.startsWith(wildcard.substring(0, firstStar)); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean matches(Identifier ident) { |
||||||
|
String test = ident.getFullName(); |
||||||
|
if (firstStar == -1) { |
||||||
|
if (wildcard.equals(test)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (!test.startsWith(wildcard.substring(0, firstStar))) |
||||||
|
return false; |
||||||
|
|
||||||
|
test = test.substring(firstStar); |
||||||
|
int lastWild = firstStar; |
||||||
|
int nextWild; |
||||||
|
while ((nextWild = wildcard.indexOf('*', lastWild + 1)) != -1) { |
||||||
|
String pattern = wildcard.substring(lastWild+1, nextWild); |
||||||
|
while (!test.startsWith(pattern)) { |
||||||
|
if (test.length() == 0) |
||||||
|
return false; |
||||||
|
test = test.substring(1); |
||||||
|
} |
||||||
|
test = test.substring(nextWild - lastWild - 1); |
||||||
|
lastWild = nextWild; |
||||||
|
} |
||||||
|
|
||||||
|
return test.endsWith(wildcard.substring(lastWild+1)); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "Wildcard "+wildcard; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue