You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1760 lines
52 KiB
1760 lines
52 KiB
/* ConstantAnalyzer Copyright (C) 1999-2002 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 net.sf.jode.obfuscator.modules;
|
|
|
|
import net.sf.jode.GlobalOptions;
|
|
import net.sf.jode.bytecode.*;
|
|
import net.sf.jode.jvm.InterpreterException;
|
|
import net.sf.jode.obfuscator.*;
|
|
import net.sf.jode.util.StringQuoter;
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.io.PrintWriter;
|
|
import java.util.BitSet;
|
|
|
|
///#def COLLECTIONS java.util
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Iterator;
|
|
///#enddef
|
|
|
|
/**
|
|
* Analyze the code, assuming every field that is not yet written to
|
|
* is constant. This may imply that some code is dead code.
|
|
*
|
|
* While we analyze the Code we remember, which local variable and
|
|
* which stack slot is constant for each instruction and if the
|
|
* instruction is dead. First we assume that every local and every
|
|
* slot is constant for each instruction, and that all instructions
|
|
* are dead code.
|
|
*
|
|
* Now we mark all local variables of the first instruction as not
|
|
* constant and the first instruction as modified.
|
|
*
|
|
* While there is a modified instruction, we pick one and repeat the
|
|
* following algorithm:
|
|
*
|
|
* If the instruction produces a constant result (because it is a ldc
|
|
* instruction, or it combines constant values), we put that instruction
|
|
* on the ConstantListener-Queue of all constant inputs and put the
|
|
* constant result on the ConstantListener-Queue of that instruction.
|
|
*
|
|
*
|
|
* @author Jochen Hoenicke */
|
|
public class ConstantAnalyzer extends SimpleAnalyzer {
|
|
|
|
private static ConstantRuntimeEnvironment runtime
|
|
= new ConstantRuntimeEnvironment();
|
|
|
|
private final static int CMP_EQ = 0;
|
|
private final static int CMP_NE = 1;
|
|
private final static int CMP_LT = 2;
|
|
private final static int CMP_GE = 3;
|
|
private final static int CMP_GT = 4;
|
|
private final static int CMP_LE = 5;
|
|
private final static int CMP_GREATER_MASK
|
|
= (1 << CMP_GT)|(1 << CMP_GE)|(1 << CMP_NE);
|
|
private final static int CMP_LESS_MASK
|
|
= (1 << CMP_LT)|(1 << CMP_LE)|(1 << CMP_NE);
|
|
private final static int CMP_EQUAL_MASK
|
|
= (1 << CMP_GE)|(1 << CMP_LE)|(1 << CMP_EQ);
|
|
|
|
final static int CONSTANT = 0x02;
|
|
final static int CONSTANTFLOW = 0x04;
|
|
|
|
/**
|
|
* The blocks, that are not analyzed yet, but whose before is
|
|
* already set.
|
|
*/
|
|
TodoQueue modifiedQueue = new TodoQueue();
|
|
|
|
/**
|
|
* The basic blocks for the current method.
|
|
*/
|
|
BasicBlocks bb;
|
|
/**
|
|
* All block infos of all blocks in the current method.
|
|
*/
|
|
BlockInfo[] infos;
|
|
/**
|
|
* The currently analyzed method, only valid while analyzeCode is running.
|
|
*/
|
|
MethodIdentifier methodIdent;
|
|
Map fieldDependencies;
|
|
|
|
Map bbInfos = new HashMap();
|
|
Map constantInfos = new HashMap();
|
|
|
|
private interface ConstantListener {
|
|
public void constantChanged();
|
|
}
|
|
|
|
private static class ConstValue implements ConstantListener {
|
|
public final static Object VOLATILE = new Object();
|
|
/**
|
|
* The constant value, VOLATILE if value is not constant.
|
|
*
|
|
* This may also be an instance of BlockInfo and point
|
|
* to the target block of the jsr, for which this variable
|
|
* contains the return value.
|
|
*/
|
|
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.
|
|
*/
|
|
Set listeners;
|
|
|
|
public ConstValue(Object constant) {
|
|
value = constant;
|
|
stackSize = (constant instanceof Double
|
|
|| constant instanceof Long) ? 2 : 1;
|
|
listeners = new HashSet();
|
|
}
|
|
|
|
public ConstValue(int stackSize) {
|
|
this.value = VOLATILE;
|
|
this.stackSize = stackSize;
|
|
}
|
|
|
|
public ConstValue(ConstValue constant) {
|
|
value = constant.value;
|
|
stackSize = constant.stackSize;
|
|
listeners = new HashSet();
|
|
constant.addConstantListener(this);
|
|
}
|
|
|
|
public ConstValue copy() {
|
|
return value == VOLATILE ? this
|
|
: new ConstValue(this);
|
|
}
|
|
|
|
/**
|
|
* Merge the other value into this value.
|
|
*/
|
|
public void merge(ConstValue other) {
|
|
if (this == other)
|
|
return;
|
|
|
|
if (value == null ? other.value == null
|
|
: value.equals(other.value)) {
|
|
if (other.value != VOLATILE)
|
|
other.addConstantListener(this);
|
|
return;
|
|
}
|
|
|
|
if (value != VOLATILE)
|
|
fireChanged();
|
|
}
|
|
|
|
public void addConstantListener(ConstantListener l) {
|
|
listeners.add(l);
|
|
}
|
|
|
|
public void fireChanged() {
|
|
value = VOLATILE;
|
|
for (Iterator i = listeners.iterator(); i.hasNext(); ) {
|
|
ConstantListener l = (ConstantListener) i.next();
|
|
// System.err.println(" notifying: "+ l);
|
|
l.constantChanged();
|
|
}
|
|
listeners = null;
|
|
}
|
|
|
|
public void constantChanged() {
|
|
if (value != VOLATILE)
|
|
fireChanged();
|
|
}
|
|
|
|
public String toString() {
|
|
String result;
|
|
if (value == VOLATILE)
|
|
result = "vol("+stackSize+")";
|
|
else if (value instanceof String)
|
|
result = StringQuoter.quote((String) value);
|
|
else
|
|
result = String.valueOf(value);
|
|
|
|
// StringBuffer sb = new StringBuffer(result).append('{');
|
|
// Iterator i = listeners.iterator();
|
|
// while (i.hasNext())
|
|
// sb.append(i.next()).append(',');
|
|
// result = sb.append('}').toString();
|
|
return result+"@"+hashCode();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class handles information necessary for jsr analysis.
|
|
* Jsr is probably the most difficult opcode to handle, we have
|
|
* to keep track of changed locals, of nested jsrs, if there is
|
|
* a corresponding ret instruction and much more.
|
|
*/
|
|
private final static class JsrInfo implements Cloneable {
|
|
BlockInfo jsrTarget;
|
|
Collection callers;
|
|
|
|
/**
|
|
* The number of outer subroutines
|
|
*/
|
|
int jsrDepth;
|
|
|
|
/**
|
|
* The outer jsr info, or null if this is a top level jsr.
|
|
*/
|
|
JsrInfo outerJsr;
|
|
|
|
/**
|
|
* The locals used in the outer subroutine, or null if this is
|
|
* a top level jsr.
|
|
* The locals used inside this jsr are in the BlockInfo itself.
|
|
*/
|
|
BitSet usedInOuter;
|
|
|
|
/**
|
|
* The info for the ret block of this subroutine.
|
|
*/
|
|
BlockInfo retInfo;
|
|
|
|
public JsrInfo(BlockInfo target, JsrInfo outer, BitSet used) {
|
|
jsrTarget = target;
|
|
if (outer != null) {
|
|
outerJsr = outer;
|
|
usedInOuter = (BitSet) used.clone();
|
|
}
|
|
jsrDepth = (outer != null ? outer.jsrDepth + 1 : 0);
|
|
callers = new ArrayList();
|
|
}
|
|
|
|
public void setRetInfo(BlockInfo retInfo) {
|
|
this.retInfo = retInfo;
|
|
for (Iterator i = callers.iterator(); i.hasNext(); )
|
|
((BlockInfo)i.next()).mergeRetLocals(this, retInfo);
|
|
}
|
|
|
|
public void addCaller(BlockInfo caller) {
|
|
if (callers.contains(caller))
|
|
return;
|
|
callers.add(caller);
|
|
if (retInfo != null)
|
|
caller.mergeRetLocals(this, retInfo);
|
|
}
|
|
|
|
public JsrInfo intersect(JsrInfo other, BitSet used) {
|
|
int otherDepth = other != null ? other.jsrDepth : -1;
|
|
JsrInfo isect = this;
|
|
int myDepth = jsrDepth;
|
|
|
|
while (otherDepth > myDepth) {
|
|
if (other.usedInOuter != null)
|
|
used.or(other.usedInOuter);
|
|
other = other.outerJsr;
|
|
otherDepth--;
|
|
}
|
|
while (myDepth > otherDepth) {
|
|
if (isect.usedInOuter != null)
|
|
used.or(isect.usedInOuter);
|
|
isect = isect.outerJsr;
|
|
myDepth--;
|
|
}
|
|
while (isect != other) {
|
|
if (other.usedInOuter != null) {
|
|
used.or(other.usedInOuter);
|
|
used.or(isect.usedInOuter);
|
|
}
|
|
other = other.outerJsr;
|
|
isect = isect.outerJsr;
|
|
}
|
|
return isect;
|
|
}
|
|
|
|
public void merge(JsrInfo outer, BitSet used) {
|
|
if (outerJsr != null)
|
|
outerJsr = outerJsr.intersect(outer, used);
|
|
if (outerJsr == null)
|
|
usedInOuter = null;
|
|
else
|
|
usedInOuter.or(used);
|
|
}
|
|
|
|
public String toString() {
|
|
StringBuffer sb = new StringBuffer(String.valueOf(jsrTarget.nr));
|
|
if (retInfo != null)
|
|
sb.append("->").append(retInfo.nr);
|
|
if (outerJsr != null)
|
|
sb.append("used="+usedInOuter+",outer=["+outerJsr+"]");
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
private static class StackLocalInfo {
|
|
ConstValue[] stack;
|
|
ConstValue[] locals;
|
|
int stackDepth;
|
|
|
|
private StackLocalInfo(ConstValue[] stack,
|
|
ConstValue[] locals, int stackDepth) {
|
|
this.stack = stack;
|
|
this.locals = locals;
|
|
this.stackDepth = stackDepth;
|
|
}
|
|
|
|
public StackLocalInfo(int maxStack, int numLocals,
|
|
boolean isStatic, String methodTypeSig) {
|
|
|
|
String[] paramTypes
|
|
= TypeSignature.getParameterTypes(methodTypeSig);
|
|
locals = new ConstValue[numLocals];
|
|
stack = new ConstValue[maxStack];
|
|
stackDepth = 0;
|
|
int slot = 0;
|
|
if (!isStatic)
|
|
locals[slot++] = unknownValue[0];
|
|
for (int i=0; i< paramTypes.length; i++) {
|
|
int stackSize = TypeSignature.getTypeSize(paramTypes[i]);
|
|
locals[slot] = unknownValue[stackSize-1];
|
|
slot += stackSize;
|
|
}
|
|
}
|
|
|
|
public StackLocalInfo(StackLocalInfo original) {
|
|
locals = new ConstValue[original.locals.length];
|
|
for (int i=0; i< locals.length; i++) {
|
|
if (original.locals[i] != null)
|
|
locals[i] = original.locals[i].copy();
|
|
}
|
|
stack = new ConstValue[original.stack.length];
|
|
stackDepth = original.stackDepth;
|
|
for (int i=0; i< stackDepth; i++) {
|
|
if (original.stack[i] != null)
|
|
stack[i] = original.stack[i].copy();
|
|
}
|
|
}
|
|
|
|
public StackLocalInfo poppush(int pops, ConstValue push) {
|
|
for (int i = pops; i > 0; i--)
|
|
stack[--stackDepth] = null;
|
|
if (push == null)
|
|
throw new NullPointerException();
|
|
stack[stackDepth] = push;
|
|
stackDepth += push.stackSize;
|
|
return this;
|
|
}
|
|
|
|
public StackLocalInfo pop(int pops) {
|
|
for (int i = pops; i > 0; i--)
|
|
stack[--stackDepth] = null;
|
|
return this;
|
|
}
|
|
|
|
public StackLocalInfo dup(int count, int depth) {
|
|
int bottom = stackDepth - count - depth;
|
|
System.arraycopy(stack, stackDepth - count,
|
|
stack, stackDepth, count);
|
|
if (depth > 0) {
|
|
System.arraycopy(stack, bottom,
|
|
stack, bottom + count, depth);
|
|
System.arraycopy(stack, stackDepth,
|
|
stack, bottom, count);
|
|
}
|
|
stackDepth += count;
|
|
return this;
|
|
}
|
|
|
|
public StackLocalInfo swap() {
|
|
ConstValue tmp = stack[stackDepth - 1];
|
|
stack[stackDepth-1] = stack[stackDepth-2];
|
|
stack[stackDepth-2] = tmp;
|
|
return this;
|
|
}
|
|
|
|
public StackLocalInfo copy() {
|
|
ConstValue[] newStack = (ConstValue[]) stack.clone();
|
|
ConstValue[] newLocals = (ConstValue[]) locals.clone();
|
|
return new StackLocalInfo(newStack, newLocals, stackDepth);
|
|
}
|
|
|
|
public ConstValue getLocal(int slot) {
|
|
return locals[slot];
|
|
}
|
|
|
|
public ConstValue getStack(int depth) {
|
|
return stack[stackDepth - depth];
|
|
}
|
|
|
|
public StackLocalInfo setLocal(int slot, ConstValue value) {
|
|
locals[slot] = value;
|
|
if (value != null && value.stackSize == 2)
|
|
locals[slot+1] = null;
|
|
return this;
|
|
}
|
|
|
|
public void mergeOneLocal(int slot, ConstValue cv) {
|
|
if (locals[slot] != null) {
|
|
if (cv == null)
|
|
// Other can be not initialized
|
|
// implies local can be not initialized
|
|
locals[slot] = null;
|
|
else
|
|
locals[slot].merge(cv);
|
|
}
|
|
}
|
|
|
|
public void merge(StackLocalInfo other) {
|
|
for (int i=0; i < locals.length; i++)
|
|
mergeOneLocal(i, other.locals[i]);
|
|
if (stack.length != other.stack.length)
|
|
throw new InternalError("stack length differs");
|
|
for (int i=0; i < stack.length; i++) {
|
|
if ((other.stack[i] == null) != (stack[i] == null))
|
|
throw new InternalError("stack types differ");
|
|
else if (stack[i] != null)
|
|
stack[i].merge(other.stack[i]);
|
|
}
|
|
}
|
|
|
|
public String toString() {
|
|
return "StackLocalInfo[locals="+Arrays.asList(locals)
|
|
+",stack="+Arrays.asList(stack)+",stackDepth="+stackDepth+"]";
|
|
}
|
|
}
|
|
|
|
private static class ConstantInfo implements ConstantListener {
|
|
ConstantInfo(int flags, Object constant) {
|
|
this.flags = flags;
|
|
this.constant = constant;
|
|
}
|
|
|
|
int flags;
|
|
/**
|
|
* The constant, may be an Instruction for CONSTANTFLOW.
|
|
*/
|
|
Object constant;
|
|
|
|
public void constantChanged() {
|
|
constant = null;
|
|
flags &= ~(CONSTANT | CONSTANTFLOW);
|
|
}
|
|
}
|
|
|
|
private static ConstValue[] unknownValue = {
|
|
new ConstValue(1), new ConstValue(2)
|
|
};
|
|
|
|
/**
|
|
* The block info contains the info needed for a single block.
|
|
*/
|
|
private class BlockInfo implements ConstantListener {
|
|
|
|
int nr;
|
|
Block block;
|
|
BlockInfo nextTodo;
|
|
|
|
int constantFlow = -1;
|
|
/**
|
|
* The state of the locals and stack before this block is
|
|
* executed.
|
|
*/
|
|
StackLocalInfo before;
|
|
|
|
/**
|
|
* The state of the locals and stack after this block is
|
|
* executed, but before the last jump instruction is done.
|
|
* So for conditional jumps the stack still contains the
|
|
* operands.
|
|
*/
|
|
StackLocalInfo after;
|
|
|
|
/**
|
|
* The JsrInfo of the innermost surrounding subroutine. If
|
|
* before is null, this value is null and means unknown. If
|
|
* before is not null, a value of null means no surrounding
|
|
* subroutine.
|
|
*/
|
|
JsrInfo jsrInfo;
|
|
|
|
/**
|
|
* The locals used between the jsr instruction for jsrInfo and
|
|
* the end of the current block; null if jsrInfo is null.
|
|
*/
|
|
BitSet usedLocals;
|
|
|
|
public BlockInfo(int nr, Block block) {
|
|
this.nr = nr;
|
|
this.block = block;
|
|
}
|
|
|
|
public boolean isReachable() {
|
|
return before != null;
|
|
}
|
|
|
|
public void mergeOneLocal(int slot, ConstValue cv) {
|
|
before.mergeOneLocal(slot, cv);
|
|
}
|
|
|
|
public void mergeBefore(StackLocalInfo info,
|
|
JsrInfo newJsrInfo,
|
|
BitSet usedLocals) {
|
|
if (before == null) {
|
|
// System.err.println("mergeBefore:::"+info);
|
|
before = new StackLocalInfo(info);
|
|
this.jsrInfo = newJsrInfo;
|
|
if (usedLocals != null)
|
|
this.usedLocals = (BitSet) usedLocals.clone();
|
|
modifiedQueue.enqueue(this);
|
|
} else {
|
|
// System.err.println("merging:::: "+before+":::AND:::"+info);
|
|
before.merge(info);
|
|
if (jsrInfo != null)
|
|
jsrInfo = jsrInfo.intersect(newJsrInfo, usedLocals);
|
|
if (jsrInfo == null)
|
|
this.usedLocals = null;
|
|
else
|
|
this.usedLocals.or(usedLocals);
|
|
if (constantFlow >= 0)
|
|
propagateAfter();
|
|
}
|
|
}
|
|
|
|
public void constantChanged() {
|
|
if (constantFlow >= 0)
|
|
propagateAfter();
|
|
}
|
|
|
|
public void useLocal(int slot) {
|
|
if (usedLocals != null)
|
|
usedLocals.set(slot);
|
|
}
|
|
|
|
public void mergeRetLocals(JsrInfo myJsrInfo, BlockInfo retBlock) {
|
|
if (constantFlow == 0) {
|
|
Instruction[] instrs = block.getInstructions();
|
|
// remove the constantFlow info
|
|
constantInfos.remove(instrs[instrs.length-1]);
|
|
constantFlow = -1;
|
|
}
|
|
|
|
Block nextBlock = block.getSuccs()[1];
|
|
if (nextBlock == null) {
|
|
/* The calling jsr is just before a return. We don't
|
|
* have to fuzz around, since nobody is interested in
|
|
* constant values.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
ConstValue[] newLocals = (ConstValue[]) after.locals.clone();
|
|
BitSet nextUsed = new BitSet();
|
|
for (int slot = 0; slot < newLocals.length; slot++) {
|
|
if (retBlock.usedLocals.get(slot)) {
|
|
newLocals[slot] = retBlock.after.locals[slot];
|
|
nextUsed.set(slot);
|
|
}
|
|
}
|
|
StackLocalInfo nextInfo
|
|
= new StackLocalInfo(after.stack, newLocals, after.stackDepth);
|
|
JsrInfo nextJsrInfo = null;
|
|
if (usedLocals != null)
|
|
nextUsed.or(usedLocals);
|
|
if (myJsrInfo.usedInOuter != null)
|
|
nextUsed.or(myJsrInfo.usedInOuter);
|
|
if (jsrInfo != null)
|
|
nextJsrInfo = jsrInfo.intersect(myJsrInfo.outerJsr,
|
|
nextUsed);
|
|
if (nextJsrInfo == null)
|
|
nextUsed = null;
|
|
|
|
infos[nextBlock.getBlockNr()].mergeBefore(nextInfo,
|
|
nextJsrInfo, nextUsed);
|
|
}
|
|
|
|
public void propagateAfter() {
|
|
Instruction[] instrs = block.getInstructions();
|
|
Instruction instr = instrs[instrs.length-1];
|
|
int opcode = instr.getOpcode();
|
|
switch (opcode) {
|
|
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;
|
|
ConstValue stacktop = after.getStack(1);
|
|
ConstValue other = null;
|
|
boolean known = stacktop.value != ConstValue.VOLATILE;
|
|
if (opcode >= opc_if_icmpeq && opcode <= opc_if_acmpne) {
|
|
other = after.getStack(2);
|
|
size = 2;
|
|
known &= other.value != ConstValue.VOLATILE;
|
|
}
|
|
StackLocalInfo nextInfo = after.copy().pop(size);
|
|
if (known) {
|
|
if (constantFlow >= 0)
|
|
/* Nothing changed... */
|
|
return;
|
|
int opc_mask;
|
|
if (opcode >= opc_if_acmpeq) {
|
|
if (opcode >= opc_ifnull) {
|
|
opc_mask = stacktop.value == null
|
|
? CMP_EQUAL_MASK : CMP_GREATER_MASK;
|
|
opcode -= opc_ifnull;
|
|
} else {
|
|
opc_mask = stacktop.value == other.value
|
|
? CMP_EQUAL_MASK : CMP_GREATER_MASK;
|
|
opcode -= opc_if_acmpeq;
|
|
}
|
|
} else {
|
|
int value = ((Integer) stacktop.value).intValue();
|
|
if (opcode >= opc_if_icmpeq) {
|
|
int val1 = ((Integer) other.value).intValue();
|
|
opc_mask = (val1 == value ? CMP_EQUAL_MASK
|
|
: val1 < value ? CMP_LESS_MASK
|
|
: CMP_GREATER_MASK);
|
|
opcode -= opc_if_icmpeq;
|
|
} else {
|
|
opc_mask = (value == 0 ? CMP_EQUAL_MASK
|
|
: value < 0 ? CMP_LESS_MASK
|
|
: CMP_GREATER_MASK);
|
|
opcode -= opc_ifeq;
|
|
}
|
|
}
|
|
|
|
constantFlow = ((opc_mask & (1<<opcode)) != 0) ? 0 : 1;
|
|
ConstantInfo constInfo = new ConstantInfo
|
|
(CONSTANTFLOW, new Integer(constantFlow));
|
|
constantInfos.put(instr, constInfo);
|
|
|
|
stacktop.addConstantListener(this);
|
|
if (other != null)
|
|
other.addConstantListener(this);
|
|
Block constantSucc = block.getSuccs()[constantFlow];
|
|
if (constantSucc != null)
|
|
infos[constantSucc.getBlockNr()]
|
|
.mergeBefore(nextInfo, jsrInfo, usedLocals);
|
|
} else {
|
|
constantInfos.remove(instr);
|
|
for (int i=0; i < 2; i++) {
|
|
if (i != constantFlow) {
|
|
Block succ = block.getSuccs()[i];
|
|
if (succ != null)
|
|
infos[succ.getBlockNr()]
|
|
.mergeBefore(nextInfo,
|
|
jsrInfo, usedLocals);
|
|
}
|
|
}
|
|
constantFlow = -1;
|
|
}
|
|
break;
|
|
}
|
|
case opc_lookupswitch: {
|
|
ConstValue stacktop = after.getStack(1);
|
|
StackLocalInfo nextInfo = after.copy().pop(1);
|
|
if (stacktop.value != ConstValue.VOLATILE) {
|
|
if (constantFlow >= 0)
|
|
/* Nothing changed... */
|
|
return;
|
|
int value = ((Integer) stacktop.value).intValue();
|
|
int[] values = instr.getValues();
|
|
constantFlow = Arrays.binarySearch(values, value);
|
|
if (constantFlow < 0)
|
|
constantFlow = values.length;
|
|
ConstantInfo constInfo = new ConstantInfo
|
|
(CONSTANTFLOW, new Integer(constantFlow));
|
|
constantInfos.put(instr, constInfo);
|
|
stacktop.addConstantListener(this);
|
|
Block constantSucc = block.getSuccs()[constantFlow];
|
|
if (constantSucc != null)
|
|
infos[constantSucc.getBlockNr()]
|
|
.mergeBefore(nextInfo, jsrInfo, usedLocals);
|
|
} else {
|
|
constantInfos.remove(instr);
|
|
Block[] succs = block.getSuccs();
|
|
for (int i=0; i < succs.length; i++) {
|
|
if (i != constantFlow) {
|
|
if (succs[i] != null)
|
|
infos[succs[i].getBlockNr()]
|
|
.mergeBefore(nextInfo, jsrInfo,
|
|
usedLocals);
|
|
}
|
|
}
|
|
constantFlow = -1;
|
|
}
|
|
break;
|
|
}
|
|
case opc_jsr: {
|
|
/* Assume there is no ret for this jsr. If the ret
|
|
* was already found this info will be corrected
|
|
* immediately by the jsrInfo.addCaller.
|
|
*/
|
|
constantFlow = 0;
|
|
ConstantInfo constInfo = new ConstantInfo
|
|
(CONSTANTFLOW, new Integer(0));
|
|
constantInfos.put(instr, constInfo);
|
|
|
|
|
|
BlockInfo target = infos[block.getSuccs()[0].getBlockNr()];
|
|
ConstValue result = new ConstValue(target);
|
|
|
|
JsrInfo newJsrInfo;
|
|
BitSet newUsed;
|
|
if (target.before == null) {
|
|
/* The info for this jsr target wasn't created before. */
|
|
newJsrInfo = new JsrInfo(target, jsrInfo, usedLocals);
|
|
newJsrInfo.addCaller(this);
|
|
newUsed = new BitSet();
|
|
} else if (target.jsrInfo == null
|
|
|| target.jsrInfo.jsrTarget != target) {
|
|
/* The jsr exits instantenously. */
|
|
newJsrInfo = jsrInfo;
|
|
newUsed = usedLocals;
|
|
} else {
|
|
/* Normal case, merge jsr infos. */
|
|
newJsrInfo = target.jsrInfo;
|
|
newJsrInfo.merge(jsrInfo, usedLocals);
|
|
newJsrInfo.addCaller(this);
|
|
newUsed = new BitSet();
|
|
}
|
|
|
|
target.mergeBefore(after.copy().poppush(0, result),
|
|
newJsrInfo, newUsed);
|
|
break;
|
|
}
|
|
case opc_ret: {
|
|
ConstValue result = after.getLocal(instr.getLocalSlot());
|
|
BlockInfo jsrTarget = (BlockInfo) result.value;
|
|
while (jsrInfo.jsrTarget != jsrTarget) {
|
|
usedLocals.or(jsrInfo.usedInOuter);
|
|
jsrInfo = jsrInfo.outerJsr;
|
|
}
|
|
jsrInfo.setRetInfo(this);
|
|
break;
|
|
}
|
|
case opc_ireturn: case opc_lreturn:
|
|
case opc_freturn: case opc_dreturn:
|
|
case opc_areturn: case opc_return:
|
|
case opc_athrow:
|
|
break;
|
|
default: {
|
|
Block succ = block.getSuccs()[0];
|
|
if (succ != null)
|
|
infos[succ.getBlockNr()].mergeBefore
|
|
(after, jsrInfo, usedLocals);
|
|
}
|
|
}
|
|
}
|
|
|
|
StackLocalInfo handleOpcode(Instruction instr, StackLocalInfo info) {
|
|
constantInfos.remove(instr);
|
|
int opcode = instr.getOpcode();
|
|
ConstValue result;
|
|
switch (opcode) {
|
|
case opc_nop:
|
|
return info.pop(0);
|
|
|
|
case opc_ldc:
|
|
case opc_ldc2_w:
|
|
result = new ConstValue(instr.getConstant());
|
|
return info.poppush(0, result);
|
|
|
|
case opc_iload: case opc_lload:
|
|
case opc_fload: case opc_dload: case opc_aload:
|
|
result = info.getLocal(instr.getLocalSlot());
|
|
if (result.value != ConstValue.VOLATILE) {
|
|
ConstantInfo constInfo
|
|
= new ConstantInfo(CONSTANT, result.value);
|
|
result.addConstantListener(constInfo);
|
|
constantInfos.put(instr, constInfo);
|
|
}
|
|
return info.poppush(0, result)
|
|
.setLocal(instr.getLocalSlot(), result);
|
|
case opc_iaload: case opc_laload:
|
|
case opc_faload: case opc_daload: case opc_aaload:
|
|
case opc_baload: case opc_caload: case opc_saload: {
|
|
result = unknownValue[(opcode == opc_laload
|
|
|| opcode == opc_daload) ? 1 : 0];
|
|
return info.poppush(2, result);
|
|
}
|
|
case opc_istore: case opc_fstore: case opc_astore: {
|
|
int slot = instr.getLocalSlot();
|
|
useLocal(slot);
|
|
result = info.getStack(1);
|
|
return info.pop(1).setLocal(slot, result);
|
|
}
|
|
case opc_lstore: case opc_dstore: {
|
|
int slot = instr.getLocalSlot();
|
|
useLocal(slot);
|
|
useLocal(slot + 1);
|
|
result = info.getStack(2);
|
|
return info.pop(2).setLocal(slot, result);
|
|
}
|
|
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;
|
|
return info.pop(2+size);
|
|
}
|
|
case opc_pop:
|
|
return info.pop(1);
|
|
case opc_pop2:
|
|
return info.pop(2);
|
|
|
|
case opc_dup: case opc_dup_x1: case opc_dup_x2:
|
|
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
|
|
return info.dup((opcode - (opc_dup - 3)) / 3,
|
|
(opcode - (opc_dup - 3)) % 3);
|
|
case opc_swap:
|
|
return info.swap();
|
|
|
|
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);
|
|
ConstValue value1 = info.getStack(2*size);
|
|
ConstValue value2 = info.getStack(1*size);
|
|
boolean known = value1.value != ConstValue.VOLATILE
|
|
&& value2.value != ConstValue.VOLATILE;
|
|
if (known) {
|
|
if (((opcode == opc_idiv || opcode == opc_irem)
|
|
&& ((Integer)value2.value).intValue() == 0)
|
|
|| ((opcode == opc_ldiv || opcode == opc_lrem)
|
|
&& ((Long)value2.value).longValue() == 0))
|
|
known = false;
|
|
}
|
|
if (known) {
|
|
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 InternalError("Can't happen.");
|
|
}
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(newValue);
|
|
result.addConstantListener(constInfo);
|
|
value1.addConstantListener(result);
|
|
value2.addConstantListener(result);
|
|
} else
|
|
result = unknownValue[size-1];
|
|
return info.poppush(2*size, result);
|
|
}
|
|
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: {
|
|
int size = 1 + (opcode - opc_ineg & 1);
|
|
ConstValue value = info.getStack(size);
|
|
if (value.value != ConstValue.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 InternalError("Can't happen.");
|
|
}
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(newValue);
|
|
result.addConstantListener(constInfo);
|
|
value.addConstantListener(result);
|
|
} else
|
|
result = unknownValue[size-1];
|
|
return info.poppush(size, result);
|
|
}
|
|
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);
|
|
ConstValue value1 = info.getStack(size+1);
|
|
ConstValue value2 = info.getStack(1);
|
|
if (value1.value != ConstValue.VOLATILE
|
|
&& value2.value != ConstValue.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 InternalError("Can't happen.");
|
|
}
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(newValue);
|
|
result.addConstantListener(constInfo);
|
|
value1.addConstantListener(result);
|
|
value2.addConstantListener(result);
|
|
} else
|
|
result = unknownValue[size-1];
|
|
return info.poppush(size+1, result);
|
|
}
|
|
case opc_iinc: {
|
|
int slot = instr.getLocalSlot();
|
|
useLocal(slot);
|
|
ConstValue local = info.getLocal(slot);
|
|
if (local.value != ConstValue.VOLATILE) {
|
|
result = new ConstValue
|
|
(new Integer(((Integer)local.value).intValue()
|
|
+ instr.getIncrement()));
|
|
local.addConstantListener(result);
|
|
} else
|
|
result = unknownValue[0];
|
|
return info.setLocal(instr.getLocalSlot(), result);
|
|
}
|
|
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);
|
|
ConstValue stack = info.getStack(insize);
|
|
if (stack.value != ConstValue.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 InternalError("Can't happen.");
|
|
}
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(newVal);
|
|
result.addConstantListener(constInfo);
|
|
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];
|
|
}
|
|
}
|
|
return info.poppush(insize, result);
|
|
}
|
|
case opc_i2b: case opc_i2c: case opc_i2s: {
|
|
ConstValue stack = info.getStack(1);
|
|
if (stack.value != ConstValue.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;
|
|
}
|
|
Integer newVal = new Integer(val);
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(newVal);
|
|
stack.addConstantListener(constInfo);
|
|
stack.addConstantListener(result);
|
|
} else
|
|
result = unknownValue[0];
|
|
return info.poppush(1, result);
|
|
}
|
|
case opc_lcmp: {
|
|
ConstValue val1 = info.getStack(4);
|
|
ConstValue val2 = info.getStack(2);
|
|
if (val1.value != ConstValue.VOLATILE
|
|
&& val2.value != ConstValue.VOLATILE) {
|
|
long value1 = ((Long) val1.value).longValue();
|
|
long value2 = ((Long) val1.value).longValue();
|
|
Integer newVal = new Integer(value1 == value2 ? 0
|
|
: value1 < value2 ? -1 : 1);
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(newVal);
|
|
result.addConstantListener(constInfo);
|
|
val1.addConstantListener(result);
|
|
val2.addConstantListener(result);
|
|
} else
|
|
result = unknownValue[0];
|
|
return info.poppush(4, result);
|
|
}
|
|
case opc_fcmpl: case opc_fcmpg: {
|
|
ConstValue val1 = info.getStack(2);
|
|
ConstValue val2 = info.getStack(1);
|
|
if (val1.value != ConstValue.VOLATILE
|
|
&& val2.value != ConstValue.VOLATILE) {
|
|
float value1 = ((Float) val1.value).floatValue();
|
|
float value2 = ((Float) val1.value).floatValue();
|
|
Integer newVal = new Integer
|
|
(value1 == value2 ? 0
|
|
: ( opcode == opc_fcmpg
|
|
? (value1 < value2 ? -1 : 1)
|
|
: (value1 > value2 ? 1 : -1)));
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(newVal);
|
|
result.addConstantListener(constInfo);
|
|
val1.addConstantListener(result);
|
|
val2.addConstantListener(result);
|
|
} else
|
|
result = unknownValue[0];
|
|
return info.poppush(2, result);
|
|
}
|
|
case opc_dcmpl: case opc_dcmpg: {
|
|
ConstValue val1 = info.getStack(4);
|
|
ConstValue val2 = info.getStack(2);
|
|
if (val1.value != ConstValue.VOLATILE
|
|
&& val2.value != ConstValue.VOLATILE) {
|
|
double value1 = ((Double) val1.value).doubleValue();
|
|
double value2 = ((Double) val1.value).doubleValue();
|
|
Integer newVal = new Integer
|
|
(value1 == value2 ? 0
|
|
: ( opcode == opc_dcmpg
|
|
? (value1 < value2 ? -1 : 1)
|
|
: (value1 > value2 ? 1 : -1)));
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(newVal);
|
|
result.addConstantListener(constInfo);
|
|
val1.addConstantListener(result);
|
|
val2.addConstantListener(result);
|
|
} else
|
|
result = unknownValue[0];
|
|
return info.poppush(4, result);
|
|
}
|
|
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:
|
|
case opc_lookupswitch:
|
|
case opc_ireturn: case opc_lreturn:
|
|
case opc_freturn: case opc_dreturn: case opc_areturn:
|
|
case opc_athrow:
|
|
case opc_jsr:
|
|
case opc_ret:
|
|
case opc_goto:
|
|
case opc_return:
|
|
return info;
|
|
|
|
case opc_putstatic:
|
|
case opc_putfield: {
|
|
final FieldIdentifier fi
|
|
= (FieldIdentifier) canonizeReference(instr);
|
|
Reference ref = instr.getReference();
|
|
int size = TypeSignature.getTypeSize(ref.getType());
|
|
if (fi != null && !fi.isNotConstant()) {
|
|
ConstValue stacktop = info.getStack(size);
|
|
Object fieldVal = fi.getConstant();
|
|
if (fieldVal == null)
|
|
fieldVal = TypeSignature.getDefaultValue(ref.getType());
|
|
if (stacktop.value == null ? fieldVal == null
|
|
: stacktop.value.equals(fieldVal)) {
|
|
stacktop.addConstantListener(new ConstantListener() {
|
|
public void constantChanged() {
|
|
fieldNotConstant(fi);
|
|
}
|
|
});
|
|
} else {
|
|
fieldNotConstant(fi);
|
|
}
|
|
}
|
|
size += (opcode == opc_putstatic) ? 0 : 1;
|
|
return info.pop(size);
|
|
}
|
|
case opc_getstatic:
|
|
case opc_getfield: {
|
|
int size = (opcode == opc_getstatic) ? 0 : 1;
|
|
FieldIdentifier fi = (FieldIdentifier) canonizeReference(instr);
|
|
Reference ref = instr.getReference();
|
|
int typesize = TypeSignature.getTypeSize(ref.getType());
|
|
if (fi != null) {
|
|
if (fi.isNotConstant()) {
|
|
fi.setReachable();
|
|
result = unknownValue[typesize - 1];
|
|
} else {
|
|
Object obj = fi.getConstant();
|
|
if (obj == null)
|
|
obj = TypeSignature.getDefaultValue(ref.getType());
|
|
ConstantInfo constInfo = new ConstantInfo(CONSTANT, obj);
|
|
constantInfos.put(instr, constInfo);
|
|
result = new ConstValue(obj);
|
|
fi.addFieldListener(methodIdent);
|
|
ConstValue prev = (ConstValue) fieldDependencies.get(fi);
|
|
if (prev != null)
|
|
prev.addConstantListener(result);
|
|
else
|
|
fieldDependencies.put(fi, result);
|
|
}
|
|
} else
|
|
result = unknownValue[typesize - 1];
|
|
return info.poppush(size, result);
|
|
}
|
|
case opc_invokespecial:
|
|
case opc_invokestatic:
|
|
case opc_invokeinterface:
|
|
case opc_invokevirtual: {
|
|
canonizeReference(instr);
|
|
Reference ref = instr.getReference();
|
|
boolean constant = true;
|
|
int size = 0;
|
|
Object cls = null;
|
|
String[] paramTypes
|
|
= TypeSignature.getParameterTypes(ref.getType());
|
|
Object[] args = new Object[paramTypes.length];
|
|
ConstValue clsValue = null;
|
|
ConstValue[] argValues = new ConstValue[paramTypes.length];
|
|
|
|
for (int i = paramTypes.length - 1; i >= 0; i--) {
|
|
size += TypeSignature.getTypeSize(paramTypes[i]);
|
|
Object value = (argValues[i] = info.getStack(size)).value;
|
|
if (value != ConstValue.VOLATILE)
|
|
args[i] = value;
|
|
else
|
|
constant = false;
|
|
}
|
|
|
|
if (opcode != opc_invokestatic) {
|
|
size++;
|
|
clsValue = info.getStack(size);
|
|
cls = clsValue.value;
|
|
if (cls == ConstValue.VOLATILE || cls == null)
|
|
constant = false;
|
|
}
|
|
String retType = TypeSignature.getReturnType(ref.getType());
|
|
if (retType.equals("V")) {
|
|
handleReference(ref, opcode == opc_invokevirtual
|
|
|| opcode == opc_invokeinterface);
|
|
return info.pop(size);
|
|
}
|
|
if (constant && !ConstantRuntimeEnvironment.isWhite(retType)) {
|
|
/* This is not a valid constant type */
|
|
constant = false;
|
|
}
|
|
Object methodResult = null;
|
|
if (constant) {
|
|
try {
|
|
methodResult = runtime.invokeMethod
|
|
(ref, opcode != opc_invokespecial, cls, args);
|
|
} catch (InterpreterException ex) {
|
|
constant = false;
|
|
if (net.sf.jode.GlobalOptions.verboseLevel > 3)
|
|
GlobalOptions.err.println("Can't interpret "+ref+": "
|
|
+ ex.getMessage());
|
|
/* result is not constant */
|
|
} catch (InvocationTargetException ex) {
|
|
constant = false;
|
|
if (net.sf.jode.GlobalOptions.verboseLevel > 3)
|
|
GlobalOptions.err.println("Method "+ref
|
|
+" throwed exception: "
|
|
+ ex.getTargetException());
|
|
/* method always throws exception ? */
|
|
}
|
|
}
|
|
ConstValue returnVal;
|
|
if (!constant) {
|
|
handleReference(ref, opcode == opc_invokevirtual
|
|
|| opcode == opc_invokeinterface);
|
|
int retsize = TypeSignature.getTypeSize(retType);
|
|
returnVal = unknownValue[retsize - 1];
|
|
} else {
|
|
ConstantInfo constInfo =
|
|
new ConstantInfo(CONSTANT, methodResult);
|
|
constantInfos.put(instr, constInfo);
|
|
returnVal = new ConstValue(methodResult);
|
|
returnVal.addConstantListener(constInfo);
|
|
if (clsValue != null)
|
|
clsValue.addConstantListener(returnVal);
|
|
for (int i=0; i< argValues.length; i++)
|
|
argValues[i].addConstantListener(returnVal);
|
|
}
|
|
return info.poppush(size, returnVal);
|
|
}
|
|
|
|
case opc_new: {
|
|
handleClass(instr.getClazzType());
|
|
return info.poppush(0, unknownValue[0]);
|
|
}
|
|
case opc_arraylength: {
|
|
return info.poppush(1, unknownValue[0]);
|
|
}
|
|
case opc_checkcast: {
|
|
handleClass(instr.getClazzType());
|
|
return info.pop(0);
|
|
}
|
|
case opc_instanceof: {
|
|
handleClass(instr.getClazzType());
|
|
return info.poppush(1, unknownValue[0]);
|
|
}
|
|
case opc_monitorenter:
|
|
case opc_monitorexit:
|
|
return info.pop(1);
|
|
case opc_multianewarray:
|
|
handleClass(instr.getClazzType());
|
|
return info.poppush(instr.getDimensions(), unknownValue[0]);
|
|
default:
|
|
throw new IllegalArgumentException("Invalid opcode "+opcode);
|
|
}
|
|
}
|
|
|
|
public void analyze() {
|
|
StackLocalInfo info = before.copy();
|
|
Handler[] handlers = block.getHandlers();
|
|
|
|
if (handlers.length > 0) {
|
|
ConstValue[] newStack = new ConstValue[info.stack.length];
|
|
newStack[0] = unknownValue[0];
|
|
StackLocalInfo catchInfo =
|
|
new StackLocalInfo(newStack, info.locals, 1);
|
|
|
|
for (int i=0; i< handlers.length; i++) {
|
|
if (handlers[i].getType() != null)
|
|
Main.getClassBundle().reachableClass
|
|
(handlers[i].getType());
|
|
|
|
infos[handlers[i].getCatcher().getBlockNr()]
|
|
.mergeBefore(catchInfo, jsrInfo, usedLocals);
|
|
}
|
|
}
|
|
|
|
Instruction[] instrs = block.getInstructions();
|
|
for (int idx = 0 ; idx < instrs.length; idx++) {
|
|
Instruction instr = instrs[idx];
|
|
info = handleOpcode(instr, info);
|
|
if (instr.isStore() && handlers.length > 0) {
|
|
int slot = instr.getLocalSlot();
|
|
ConstValue newValue = info.locals[slot];
|
|
for (int i=0; i< handlers.length; i++) {
|
|
infos[handlers[i].getCatcher().getBlockNr()]
|
|
.mergeOneLocal(slot, info.locals[slot]);
|
|
if (newValue.stackSize > 1)
|
|
infos[handlers[i].getCatcher().getBlockNr()]
|
|
.mergeOneLocal(slot+1, info.locals[slot+1]);
|
|
}
|
|
}
|
|
}
|
|
after = info;
|
|
propagateAfter();
|
|
}
|
|
|
|
public void dumpInfo(PrintWriter output) {
|
|
output.println("/-["+nr+"]-"+before);
|
|
if (constantFlow >= 0)
|
|
output.println("| constantFlow: "+constantFlow);
|
|
if (jsrInfo != null)
|
|
output.println("| used: "+usedLocals+" JSR: "+jsrInfo);
|
|
block.dumpCode(output);
|
|
output.println("\\-["+nr+"]-"+after);
|
|
}
|
|
|
|
public String toString() {
|
|
return "BlockAnalyzer["+nr+"]";
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* The TodoQueue is a linked list of BlockInfo
|
|
*
|
|
* There is only one TodoQueue, the modifiedQueue in analyzeCode
|
|
*
|
|
* The queue operations are in StackLocalInfo.
|
|
*/
|
|
static class TodoQueue {
|
|
BlockInfo first;
|
|
|
|
public void enqueue(BlockInfo info) {
|
|
if (info.nextTodo == null) {
|
|
info.nextTodo = first;
|
|
first = info;
|
|
}
|
|
}
|
|
|
|
public BlockInfo dequeue() {
|
|
BlockInfo result = first;
|
|
if (result != null) {
|
|
first = result.nextTodo;
|
|
result.nextTodo = null;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public void fieldNotConstant(FieldIdentifier fi) {
|
|
ConstValue value = (ConstValue) fieldDependencies.remove(fi);
|
|
if (value != null)
|
|
value.constantChanged();
|
|
fi.removeFieldListener(methodIdent);
|
|
fi.setNotConstant();
|
|
}
|
|
|
|
void handleReference(Reference ref, boolean isVirtual) {
|
|
Main.getClassBundle().reachableReference(ref, isVirtual);
|
|
}
|
|
|
|
void handleClass(String clName) {
|
|
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);
|
|
Main.getClassBundle().reachableClass(clName);
|
|
}
|
|
}
|
|
|
|
public ConstantAnalyzer() {
|
|
}
|
|
|
|
|
|
public void dumpBlockInfo(PrintWriter output) {
|
|
for (int i=0; i < infos.length; i++)
|
|
infos[i].dumpInfo(output);
|
|
}
|
|
|
|
public void analyzeCode(MethodIdentifier methodIdent, BasicBlocks bb) {
|
|
Block[] blocks = bb.getBlocks();
|
|
this.methodIdent = methodIdent;
|
|
this.bb = bb;
|
|
this.infos = new BlockInfo[blocks.length];
|
|
this.fieldDependencies = new HashMap();
|
|
|
|
for (int i=0; i< infos.length; i++)
|
|
infos[i] = new BlockInfo(i, blocks[i]);
|
|
|
|
Block startBlock = bb.getStartBlock();
|
|
if (startBlock != null) {
|
|
MethodInfo minfo = bb.getMethodInfo();
|
|
infos[startBlock.getBlockNr()].mergeBefore
|
|
(new StackLocalInfo(bb.getMaxStack(), bb.getMaxLocals(),
|
|
minfo.isStatic(), minfo.getType()),
|
|
null, null);
|
|
|
|
BlockInfo info;
|
|
while ((info = modifiedQueue.dequeue()) != null) {
|
|
// dumpBlockInfo(GlobalOptions.err);
|
|
// GlobalOptions.err.println("Analyzing: "+info);
|
|
info.analyze();
|
|
}
|
|
}
|
|
|
|
// GlobalOptions.err.println("After Analyze");
|
|
// dumpBlockInfo(GlobalOptions.err);
|
|
|
|
BitSet reachableBlocks = new BitSet();
|
|
for (int i=0; i< infos.length; i++) {
|
|
if (infos[i].isReachable())
|
|
reachableBlocks.set(i);
|
|
}
|
|
bbInfos.put(bb, reachableBlocks);
|
|
|
|
this.methodIdent = null;
|
|
this.bb = null;
|
|
this.infos = null;
|
|
this.fieldDependencies = null;
|
|
}
|
|
|
|
public static void replaceWith(ArrayList newCode, Instruction instr,
|
|
Instruction replacement) {
|
|
switch(instr.getOpcode()) {
|
|
case opc_jsr:
|
|
newCode.add(Instruction.forOpcode(opc_ldc, (Object) null));
|
|
break;
|
|
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:
|
|
if (replacement != null)
|
|
newCode.add(replacement);
|
|
return;
|
|
case opc_ifeq: case opc_ifne:
|
|
case opc_iflt: case opc_ifge:
|
|
case opc_ifgt: case opc_ifle:
|
|
case opc_ifnull: case opc_ifnonnull:
|
|
case opc_arraylength:
|
|
case opc_lookupswitch:
|
|
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:
|
|
newCode.add(Instruction.forOpcode(opc_pop));
|
|
break;
|
|
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_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:
|
|
newCode.add(Instruction.forOpcode(opc_pop2));
|
|
/* 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:
|
|
newCode.add(Instruction.forOpcode(opc_pop2));
|
|
break;
|
|
|
|
case opc_lshl: case opc_lshr: case opc_lushr:
|
|
newCode.add(Instruction.forOpcode(opc_pop));
|
|
newCode.add(Instruction.forOpcode(opc_pop2));
|
|
break;
|
|
case opc_putstatic:
|
|
case opc_putfield:
|
|
if (TypeSignature
|
|
.getTypeSize(instr.getReference().getType()) == 2) {
|
|
newCode.add(Instruction.forOpcode(opc_pop2));
|
|
if (instr.getOpcode() == opc_putfield)
|
|
newCode.add(Instruction.forOpcode(opc_pop));
|
|
} else
|
|
newCode.add(Instruction.forOpcode(instr.getOpcode()
|
|
== opc_putfield
|
|
? opc_pop2 : opc_pop));
|
|
break;
|
|
case opc_invokespecial:
|
|
case opc_invokestatic:
|
|
case opc_invokeinterface:
|
|
case opc_invokevirtual: {
|
|
Reference ref = instr.getReference();
|
|
String[] pt = TypeSignature.getParameterTypes(ref.getType());
|
|
int i = pt.length;
|
|
while (i > 0)
|
|
newCode.add(Instruction.forOpcode(TypeSignature
|
|
.getTypeSize(pt[--i])
|
|
+ opc_pop - 1));
|
|
|
|
if (instr.getOpcode() != opc_invokestatic)
|
|
newCode.add(Instruction.forOpcode(opc_pop));
|
|
break;
|
|
}
|
|
default:
|
|
throw new InternalError("Unexpected opcode");
|
|
}
|
|
if (replacement != null)
|
|
newCode.add(replacement);
|
|
}
|
|
|
|
public void transformCode(BasicBlocks bb) {
|
|
BitSet reachable = (BitSet) bbInfos.remove(bb);
|
|
Block[] blocks = bb.getBlocks();
|
|
Handler[] handlers = bb.getExceptionHandlers();
|
|
|
|
Block newStartBlock = bb.getStartBlock();
|
|
int newBlockCtr = 0;
|
|
int newHandlerCtr = 0;
|
|
next_handler:
|
|
for (int i = 0; i < handlers.length; i++) {
|
|
int start = handlers[i].getStart().getBlockNr();
|
|
int end = handlers[i].getEnd().getBlockNr();
|
|
while (!reachable.get(end)) {
|
|
if (start == end)
|
|
/* handler not reachable, check next one. */
|
|
continue next_handler;
|
|
start++;
|
|
}
|
|
while (!reachable.get(end)) {
|
|
end--;
|
|
}
|
|
handlers[i].setStart(blocks[start]);
|
|
handlers[i].setEnd(blocks[start]);
|
|
/* Catcher is always reachable */
|
|
handlers[newHandlerCtr++] = handlers[i];
|
|
}
|
|
for (int i=0; i < blocks.length; i++) {
|
|
if (!reachable.get(i))
|
|
continue;
|
|
blocks[newBlockCtr] = blocks[i];
|
|
Instruction[] oldCode = blocks[i].getInstructions();
|
|
Block[] succs = blocks[i].getSuccs();
|
|
ArrayList newCode = new ArrayList(oldCode.length);
|
|
for (int idx = 0; idx < oldCode.length; idx++) {
|
|
Instruction instr = oldCode[idx];
|
|
ConstantInfo info = (ConstantInfo) constantInfos.remove(instr);
|
|
if (info != null && (info.flags & CONSTANT) != 0) {
|
|
Instruction ldcInstr = Instruction.forOpcode
|
|
(info.constant instanceof Long
|
|
|| info.constant instanceof Double
|
|
? opc_ldc2_w : opc_ldc, info.constant);
|
|
if (GlobalOptions.verboseLevel > 2)
|
|
GlobalOptions.err.println
|
|
(bb + ": Replacing " + instr
|
|
+ " with constant " + info.constant);
|
|
replaceWith(newCode, instr, ldcInstr);
|
|
} else if (info != null && (info.flags & CONSTANTFLOW) != 0) {
|
|
int succnr = ((Integer)info.constant).intValue();
|
|
replaceWith(newCode, instr, null);
|
|
if (GlobalOptions.verboseLevel > 2)
|
|
GlobalOptions.err.println
|
|
(bb + ": Removing " + instr);
|
|
succs = new Block[] { succs[succnr] };
|
|
} else {
|
|
int opcode = instr.getOpcode();
|
|
switch (opcode) {
|
|
case opc_nop:
|
|
break;
|
|
|
|
case opc_putstatic:
|
|
case opc_putfield: {
|
|
Reference ref = instr.getReference();
|
|
FieldIdentifier fi = (FieldIdentifier)
|
|
Main.getClassBundle().getIdentifier(ref);
|
|
if (fi != null
|
|
&& (Main.stripping & Main.STRIP_UNREACH) != 0
|
|
&& !fi.isReachable()) {
|
|
replaceWith(newCode, instr, null);
|
|
break;
|
|
}
|
|
/* fall through */
|
|
}
|
|
default:
|
|
newCode.add(instr);
|
|
}
|
|
}
|
|
}
|
|
blocks[i].setCode((Instruction[]) newCode.toArray(new Instruction[newCode.size()]), succs);
|
|
newBlockCtr++;
|
|
}
|
|
if (newBlockCtr < blocks.length) {
|
|
Block[] newBlocks = new Block[newBlockCtr];
|
|
System.arraycopy(blocks, 0, newBlocks, 0, newBlockCtr);
|
|
blocks = newBlocks;
|
|
}
|
|
if (newHandlerCtr < handlers.length) {
|
|
Handler[] newHandlers = new Handler[newHandlerCtr];
|
|
System.arraycopy(handlers, 0, newHandlers, 0, newHandlerCtr);
|
|
handlers = newHandlers;
|
|
}
|
|
bb.setBlocks(blocks, newStartBlock, handlers);
|
|
}
|
|
}
|
|
|