git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@6 379699f6-c40d-0410-875b-85095c16579estable
parent
fbd4236fed
commit
1995b7a078
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,106 @@ |
|||||||
package jode; |
package jode; |
||||||
import sun.tools.java.*; |
import sun.tools.java.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* The LocalInfo represents a local variable of a method. |
||||||
|
* The problem is that two different local variables may use the same |
||||||
|
* slot. The current strategie is to make the range of a local variable |
||||||
|
* as small as possible. |
||||||
|
* |
||||||
|
* There may be more than one LocalInfo for a single local variable, |
||||||
|
* because the algorithm begins with totally disjunct variables and |
||||||
|
* then unifies locals. One of the local is then a shadow object which |
||||||
|
* calls the member functions of the other local. |
||||||
|
*/ |
||||||
public class LocalInfo { |
public class LocalInfo { |
||||||
static int serialnr = 0; |
private static int serialnr = 0; |
||||||
Identifier name; |
private Identifier name; |
||||||
Type type; |
private Type type; |
||||||
|
private LocalInfo shadow; |
||||||
|
|
||||||
public LocalInfo() { |
/** |
||||||
name = Identifier.lookup("__"+serialnr); |
* Create a new local info. The name will be an identifier |
||||||
type = Type.tUnknown; |
* of the form local_x__yyy, where x is the slot number and |
||||||
|
* yyy a unique number. |
||||||
|
* @param slot The slot of this variable. |
||||||
|
*/ |
||||||
|
public LocalInfo(int slot) { |
||||||
|
name = Identifier.lookup("local_"+slot+"__"+serialnr); |
||||||
|
type = MyType.tUnknown; |
||||||
serialnr++; |
serialnr++; |
||||||
} |
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Combines the LocalInfo with another. This will make this |
||||||
|
* a shadow object to the other local info. That is all member |
||||||
|
* functions will use the new local info instead of data in this |
||||||
|
* object. <p> |
||||||
|
* If this is called with ourself nothing will happen. |
||||||
|
* @param li the local info that we want to shadow. |
||||||
|
*/ |
||||||
|
public void combineWith(LocalInfo li) { |
||||||
|
if (this == li) |
||||||
|
return; |
||||||
|
if (shadow != null) |
||||||
|
shadow.combineWith(li); |
||||||
|
shadow = li; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the real LocalInfo. This may be different from the |
||||||
|
* current object if this is a shadow local info. |
||||||
|
*/ |
||||||
|
public LocalInfo getLocalInfo() { |
||||||
|
if (shadow != null) |
||||||
|
return shadow.getLocalInfo(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the name of this local. |
||||||
|
*/ |
||||||
|
public Identifier getName() { |
||||||
|
if (shadow != null) |
||||||
|
return shadow.getName(); |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the name of this local. |
||||||
|
*/ |
||||||
|
public void setName(Identifier name) { |
||||||
|
if (shadow != null) |
||||||
|
shadow.setName(name); |
||||||
|
else |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the type of this local. |
||||||
|
*/ |
||||||
|
public Type getType() { |
||||||
|
if (shadow != null) |
||||||
|
return shadow.getType(); |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets a new information about the type of this local. |
||||||
|
* The type of the local is may be made more specific by this call. |
||||||
|
* @param The new type information to be set. |
||||||
|
* @return The new type of the local. |
||||||
|
*/ |
||||||
|
public Type setType(Type newType) { |
||||||
|
if (shadow != null) |
||||||
|
return shadow.setType(type); |
||||||
|
this.type = MyType.intersection(this.type, newType); |
||||||
|
if (this.type == MyType.tError) |
||||||
|
System.err.println("Type error in "+name.toString()); |
||||||
|
return this.type; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isShadow() { |
||||||
|
return (shadow != null); |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
|
@ -0,0 +1,206 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.*; |
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
public class LocalVariableAnalyzer { |
||||||
|
|
||||||
|
Vector locals; |
||||||
|
|
||||||
|
LocalInfo[] argLocals; |
||||||
|
LocalVariableTable lvt; |
||||||
|
JodeEnvironment env; |
||||||
|
FieldDefinition mdef; |
||||||
|
int maxlocals; |
||||||
|
|
||||||
|
LocalVariableAnalyzer(JodeEnvironment env, FieldDefinition mdef, |
||||||
|
int maxlocals) { |
||||||
|
this.env = env; |
||||||
|
this.mdef = mdef; |
||||||
|
this.maxlocals = maxlocals; |
||||||
|
locals = new Vector(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads the local variable table from the class file |
||||||
|
* if it exists. |
||||||
|
*/ |
||||||
|
public void read(BinaryCode bc) { |
||||||
|
BinaryAttribute attr = bc.getAttributes(); |
||||||
|
while (attr != null) { |
||||||
|
if (attr.getName() == Constants.idLocalVariableTable) { |
||||||
|
DataInputStream stream = |
||||||
|
new DataInputStream |
||||||
|
(new ByteArrayInputStream(attr.getData())); |
||||||
|
try { |
||||||
|
lvt = new LocalVariableTable(maxlocals); |
||||||
|
lvt.read(env, stream); |
||||||
|
} catch (IOException ex) { |
||||||
|
throw new ClassFormatError(ex.toString()); |
||||||
|
} |
||||||
|
} |
||||||
|
attr = attr.getNextAttribute(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method combines to LocalInfos to a single one. |
||||||
|
* It also handles the special cases where one or both LocalInfo |
||||||
|
* are null. |
||||||
|
*/ |
||||||
|
private LocalInfo combine(int slot, LocalInfo li1, LocalInfo li2) { |
||||||
|
if (li1 == null && li2 == null) { |
||||||
|
li2 = new LocalInfo(slot); |
||||||
|
locals.addElement(li2); |
||||||
|
} else if (li1 != null && li2 != null) |
||||||
|
li1.combineWith(li2.getLocalInfo()); |
||||||
|
else if (li2 == null) |
||||||
|
li2 = li1; |
||||||
|
|
||||||
|
return li2.getLocalInfo(); |
||||||
|
} |
||||||
|
|
||||||
|
public void analyze(MethodInstructionHeader mih) { |
||||||
|
|
||||||
|
Hashtable done = new Hashtable(); |
||||||
|
Stack instrStack = new Stack(); |
||||||
|
Stack readsStack = new Stack(); |
||||||
|
|
||||||
|
LocalInfo[] reads = new LocalInfo[maxlocals]; |
||||||
|
|
||||||
|
Enumeration predec = mih.getPredecessors().elements(); |
||||||
|
while (predec.hasMoreElements()) { |
||||||
|
instrStack.push(predec.nextElement()); |
||||||
|
readsStack.push(reads); |
||||||
|
} |
||||||
|
|
||||||
|
while (!instrStack.empty()) { |
||||||
|
InstructionHeader instr = |
||||||
|
(InstructionHeader) instrStack.pop(); |
||||||
|
LocalInfo[] prevReads = |
||||||
|
(LocalInfo[]) done.get(instr); |
||||||
|
reads = (LocalInfo[]) readsStack.pop(); |
||||||
|
|
||||||
|
if (env.isVerbose) |
||||||
|
System.err.print("."); |
||||||
|
// System.err.println("");
|
||||||
|
// System.err.print("Addr: "+instr.getAddress()+ " [");
|
||||||
|
// for (int i=0; i< maxlocals; i++) {
|
||||||
|
// if (reads[i] != null)
|
||||||
|
// System.err.print(", "+reads[i].getName().toString());
|
||||||
|
// }
|
||||||
|
// System.err.print("] ");
|
||||||
|
|
||||||
|
if (prevReads != null) { |
||||||
|
boolean changed = false; |
||||||
|
for (int i=0; i<maxlocals; i++) { |
||||||
|
if (reads[i] != null) { |
||||||
|
reads[i] = reads[i].getLocalInfo(); |
||||||
|
if (prevReads[i] == null) { |
||||||
|
changed = true; |
||||||
|
} else if (prevReads[i].getLocalInfo() != reads[i]) { |
||||||
|
prevReads[i].combineWith(reads[i]); |
||||||
|
changed = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (!changed) |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (!(instr instanceof MethodInstructionHeader)) { |
||||||
|
if (instr.getInstruction() instanceof LocalVarOperator) { |
||||||
|
LocalVarOperator op = |
||||||
|
(LocalVarOperator)instr.getInstruction(); |
||||||
|
int slot = op.getSlot(); |
||||||
|
|
||||||
|
LocalInfo li = combine(slot, op.getLocalInfo(), |
||||||
|
reads[slot]); |
||||||
|
|
||||||
|
LocalInfo[] newReads = new LocalInfo[maxlocals]; |
||||||
|
System.arraycopy(reads, 0, newReads, 0, maxlocals); |
||||||
|
|
||||||
|
op.setLocalInfo(li); |
||||||
|
if (op.isRead()) |
||||||
|
newReads[slot] = li; |
||||||
|
else |
||||||
|
newReads[slot] = null; |
||||||
|
|
||||||
|
reads = newReads; |
||||||
|
} |
||||||
|
|
||||||
|
predec = instr.getPredecessors().elements(); |
||||||
|
while (predec.hasMoreElements()) { |
||||||
|
instrStack.push(predec.nextElement()); |
||||||
|
readsStack.push(reads); |
||||||
|
} |
||||||
|
} |
||||||
|
done.put(instr, reads); |
||||||
|
} |
||||||
|
// System.err.println("");
|
||||||
|
|
||||||
|
reads = (LocalInfo[]) done.get(mih); |
||||||
|
|
||||||
|
|
||||||
|
Type[] paramTypes = mdef.getType().getArgumentTypes(); |
||||||
|
int length = (mdef.isStatic() ? 0 : 1) + paramTypes.length; |
||||||
|
argLocals = new LocalInfo[length]; |
||||||
|
int offset = 0; |
||||||
|
if (!mdef.isStatic()) { |
||||||
|
LocalInfo li = reads[0]; |
||||||
|
if (li == null) |
||||||
|
li = new LocalInfo(0); |
||||||
|
li.setName(Constants.idThis); |
||||||
|
li.setType(mdef.getClassDefinition().getType()); |
||||||
|
argLocals[0] = li.getLocalInfo(); |
||||||
|
offset++; |
||||||
|
} |
||||||
|
for (int i=0; i< paramTypes.length; i++) { |
||||||
|
LocalInfo li = reads[i+offset]; |
||||||
|
if (li == null) |
||||||
|
li = new LocalInfo(i+offset); |
||||||
|
li.setType(paramTypes[i]); |
||||||
|
argLocals[offset+i] = li.getLocalInfo(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void createLocalInfo(CodeAnalyzer code) { |
||||||
|
// System.err.println("createLocalInfo");
|
||||||
|
MethodInstructionHeader mih = code.methodHeader; |
||||||
|
if (lvt == null) |
||||||
|
analyze(mih); |
||||||
|
else { |
||||||
|
int length = (mdef.isStatic() ? 0 : 1) + |
||||||
|
mdef.getType().getArgumentTypes().length; |
||||||
|
argLocals = new LocalInfo[length]; |
||||||
|
for (int i=0; i < length; i++) |
||||||
|
argLocals[i] = lvt.getLocal(i).getInfo(-1); |
||||||
|
for (InstructionHeader ih = mih.getNextInstruction(); |
||||||
|
ih != null; ih = ih.getNextInstruction()) { |
||||||
|
if (ih.getInstruction() instanceof LocalVarOperator) { |
||||||
|
LocalVarOperator op = |
||||||
|
(LocalVarOperator)ih.getInstruction(); |
||||||
|
int slot = op.getSlot(); |
||||||
|
LocalInfo li = lvt.getLocal(slot).getInfo(ih.getAddress()); |
||||||
|
op.setLocalInfo(li); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public LocalInfo getLocal(int i) { |
||||||
|
return argLocals[i]; |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpSource(TabbedPrintWriter writer) |
||||||
|
throws java.io.IOException |
||||||
|
{ |
||||||
|
Enumeration enum = locals.elements(); |
||||||
|
while (enum.hasMoreElements()) { |
||||||
|
LocalInfo li = (LocalInfo) enum.nextElement(); |
||||||
|
if (!li.isShadow()) |
||||||
|
writer.println(env.getTypeString(li.getType(), |
||||||
|
li.getName())+";"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,27 +1,32 @@ |
|||||||
package jode; |
package jode; |
||||||
import sun.tools.java.Type; |
import sun.tools.java.Type; |
||||||
|
|
||||||
public class CompareToIntOperator extends BinaryOperator { |
public class CompareToIntOperator extends SimpleOperator { |
||||||
public CompareToIntOperator(int addr, int length, Type type, int op) { |
public CompareToIntOperator(Type type, int lessGreater) { |
||||||
super(addr,length, Type.tInt, op); |
super(Type.tInt, 0, 2); |
||||||
operandType = type; |
operandTypes[0] = operandTypes[1] = type; |
||||||
} |
} |
||||||
|
|
||||||
public int getPriority() { |
public int getPriority() { |
||||||
switch (getOperator()) { |
return 499; |
||||||
case 25: |
} |
||||||
case 26: |
|
||||||
return 500; |
public int getOperandPriority(int i) { |
||||||
case 27: |
|
||||||
case 28: |
|
||||||
case 29: |
|
||||||
case 30: |
|
||||||
return 550; |
return 550; |
||||||
} |
} |
||||||
throw new RuntimeException("Illegal operator"); |
|
||||||
|
public void setOperandType(Type[] inputTypes) { |
||||||
|
super.setOperandType(inputTypes); |
||||||
|
Type operandType = |
||||||
|
MyType.intersection(operandTypes[0],operandTypes[1]); |
||||||
|
operandTypes[0] = operandTypes[1] = operandType; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean equals(Object o) { |
||||||
|
return (o instanceof CompareToIntOperator); |
||||||
} |
} |
||||||
|
|
||||||
public String toString(CodeAnalyzer ca, String[] operands) { |
public String toString(String[] operands) { |
||||||
return operands[0] + " "+opString[operator]+" "+operands[1]; |
return operands[0] + " <=> " + operands[1]; |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,30 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
public class ConstantArrayOperator extends SimpleOperator { |
||||||
|
|
||||||
|
public ConstantArrayOperator(Type type, int size) { |
||||||
|
super(type, 0, size); |
||||||
|
for (int i=0; i< size; i++) |
||||||
|
operandTypes[i] = type.getElementType(); |
||||||
|
} |
||||||
|
|
||||||
|
public int getPriority() { |
||||||
|
return 200; |
||||||
|
} |
||||||
|
|
||||||
|
public int getOperandPriority(int i) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString(String[] operands) { |
||||||
|
StringBuffer result |
||||||
|
= new StringBuffer("{"); |
||||||
|
for (int i=0; i< getOperandCount(); i++) { |
||||||
|
if (i>0) |
||||||
|
result.append(", "); |
||||||
|
result.append(operands[i]); |
||||||
|
} |
||||||
|
return result.append("}").toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
public class EmptyStringOperator extends NoArgOperator { |
||||||
|
|
||||||
|
public EmptyStringOperator() { |
||||||
|
super(Type.tString, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public int getPriority() { |
||||||
|
return 1000; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean equals(Object o) { |
||||||
|
return (o instanceof EmptyStringOperator); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString(String[] operands) { |
||||||
|
return "\"\""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,55 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
public class IIncOperator extends NoArgOperator |
||||||
|
implements LocalVarOperator { |
||||||
|
int slot; |
||||||
|
String value; |
||||||
|
LocalInfo local; |
||||||
|
|
||||||
|
public IIncOperator(int slot, String value, int operator) { |
||||||
|
super(MyType.tVoid, operator); |
||||||
|
this.slot = slot; |
||||||
|
this.value = value; |
||||||
|
} |
||||||
|
|
||||||
|
public String getValue() { |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isRead() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isWrite() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLocalInfo(LocalInfo local) { |
||||||
|
local.setType(MyType.tUIndex); |
||||||
|
this.local = local; |
||||||
|
} |
||||||
|
|
||||||
|
public LocalInfo getLocalInfo() { |
||||||
|
return local; |
||||||
|
} |
||||||
|
|
||||||
|
public int getSlot() { |
||||||
|
return slot; |
||||||
|
} |
||||||
|
|
||||||
|
public int getPriority() { |
||||||
|
return 100; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean matches(Operator loadop) { |
||||||
|
return loadop instanceof LocalLoadOperator && |
||||||
|
((LocalLoadOperator)loadop).getLocalInfo().getLocalInfo() |
||||||
|
== local.getLocalInfo(); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString(String[] operands) { |
||||||
|
return local.getName().toString() + |
||||||
|
getOperatorString() + value; |
||||||
|
} |
||||||
|
} |
@ -1,34 +1,20 @@ |
|||||||
package jode; |
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
public abstract class Instruction { |
public abstract class Instruction { |
||||||
int addr,length; |
protected Type type; |
||||||
|
|
||||||
Instruction(int a, int l) { |
Instruction(Type type) { |
||||||
addr = a; |
this.type = type; |
||||||
length = l; |
|
||||||
} |
} |
||||||
|
|
||||||
public int getAddr() { |
public Type getType() { |
||||||
return addr; |
return type; |
||||||
} |
} |
||||||
|
|
||||||
public void setAddr(int addr) { |
public Instruction simplify() { |
||||||
this.addr = addr; |
return this; |
||||||
} |
} |
||||||
|
|
||||||
public int getLength() { |
public abstract String toString(); |
||||||
return length; |
|
||||||
} |
|
||||||
|
|
||||||
public void setLength(int length) { |
|
||||||
this.length = length; |
|
||||||
} |
|
||||||
|
|
||||||
public int[] getSuccessors() { |
|
||||||
int[] result = { addr + length }; |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
public abstract void dumpSource(TabbedPrintWriter tpw, CodeAnalyzer ca) |
|
||||||
throws java.io.IOException; |
|
||||||
} |
} |
||||||
|
@ -0,0 +1,282 @@ |
|||||||
|
package jode; |
||||||
|
import java.util.Vector; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class maintains the connections between the |
||||||
|
* InstructionHeaders. They are connected in a doubly linked list |
||||||
|
* (but a instruction may have multiple successors and predecessors). |
||||||
|
* @see JumpInstructionHeader |
||||||
|
* @see SwitchInstructionHeader |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class InstructionHeader { |
||||||
|
int addr, length; |
||||||
|
Instruction instr; |
||||||
|
InstructionHeader nextInstruction; |
||||||
|
|
||||||
|
Type switchType; |
||||||
|
int[] cases; |
||||||
|
int[] succs; |
||||||
|
InstructionHeader[] successors; |
||||||
|
|
||||||
|
Vector predecessors = new Vector(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new InstructionHeader. |
||||||
|
* @param addr The address of this Instruction. |
||||||
|
* @param length The length of this Instruction. |
||||||
|
* @param instr The underlying Instruction. |
||||||
|
*/ |
||||||
|
public InstructionHeader(int addr, int length, Instruction instr) { |
||||||
|
int[] succs = { addr + length }; |
||||||
|
this.addr = addr; |
||||||
|
this.length = length; |
||||||
|
this.instr = instr; |
||||||
|
switchType = MyType.tVoid; |
||||||
|
this.cases = new int[0]; |
||||||
|
this.succs = succs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new InstructionHeader. |
||||||
|
* @param addr The address of this Instruction. |
||||||
|
* @param length The length of this Instruction. |
||||||
|
* @param instr The underlying Instruction. |
||||||
|
* @param type The type of the switch |
||||||
|
* @param cases The possible cases |
||||||
|
* @param succs The destinations (one longer for default) |
||||||
|
*/ |
||||||
|
public InstructionHeader(int addr, int length, Instruction instr, |
||||||
|
Type type, int[] cases, int[] succs) { |
||||||
|
this.addr = addr; |
||||||
|
this.length = length; |
||||||
|
this.instr = instr; |
||||||
|
switchType = type; |
||||||
|
this.cases = cases; |
||||||
|
this.succs = succs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an InstructionHeader for a return |
||||||
|
* @param addr The address of this instruction. |
||||||
|
* @param length The length of this instruction. |
||||||
|
* @param instr The underlying Instruction. |
||||||
|
*/ |
||||||
|
public static InstructionHeader ret(int addr, int length, |
||||||
|
Instruction instr) { |
||||||
|
return new InstructionHeader (addr, length, instr, |
||||||
|
MyType.tVoid, new int[0], new int[0]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an InstructionHeader for an unconditional jump. |
||||||
|
* @param addr The address of this instruction. |
||||||
|
* @param length The length of this instruction. |
||||||
|
* @param instr The underlying Instruction. |
||||||
|
* @param dest The destination address of the jump. |
||||||
|
*/ |
||||||
|
public static InstructionHeader jump(int addr, int length, int dest, |
||||||
|
Instruction instr) { |
||||||
|
int [] succs = { dest }; |
||||||
|
return new InstructionHeader (addr, length, instr, |
||||||
|
MyType.tVoid, new int[0], succs); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an InstructionHeader for a conditional jump. |
||||||
|
* @param addr The address of this instruction. |
||||||
|
* @param length The length of this instruction. |
||||||
|
* @param instr The underlying Instruction. |
||||||
|
* @param dest The destination address of the jump. |
||||||
|
*/ |
||||||
|
public static InstructionHeader conditional(int addr, int length, int dest, |
||||||
|
Instruction instr) { |
||||||
|
int[] cases = { 0 }; |
||||||
|
int[] succs = { addr+length , dest }; |
||||||
|
return new InstructionHeader (addr, length, instr, |
||||||
|
MyType.tBoolean, cases, succs); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return instr.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the address of this instruction. |
||||||
|
* @return The address. |
||||||
|
*/ |
||||||
|
public int getAddress() { |
||||||
|
return addr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the next address in code order. |
||||||
|
* @return The next instruction |
||||||
|
*/ |
||||||
|
public int getNextAddr() { |
||||||
|
return addr+length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the underlying instruction. |
||||||
|
* @return The underlying instruction. |
||||||
|
*/ |
||||||
|
public Instruction getInstruction() { |
||||||
|
return instr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the next instruction in code order. This function mustn't |
||||||
|
* be called before resolveSuccessors is executed for this |
||||||
|
* InstructionHeaders. |
||||||
|
* @return The next instruction |
||||||
|
*/ |
||||||
|
public InstructionHeader getNextInstruction() { |
||||||
|
return nextInstruction; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the successors of this instructions. This function mustn't |
||||||
|
* be called before resolveSuccessors is executed for this |
||||||
|
* InstructionHeaders. |
||||||
|
* @return Array of successors. |
||||||
|
*/ |
||||||
|
public InstructionHeader[] getSuccessors() { |
||||||
|
return successors; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasDirectPredecessor() { |
||||||
|
return predecessors.size() == 1 && |
||||||
|
((InstructionHeader)predecessors.elementAt(0)). |
||||||
|
getNextInstruction() == this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the unique predecessor or null if there isn't a |
||||||
|
* unique predecessor. |
||||||
|
*/ |
||||||
|
public InstructionHeader getUniquePredecessor() { |
||||||
|
if (predecessors.size() != 1) |
||||||
|
return null; |
||||||
|
InstructionHeader pre = (InstructionHeader)predecessors.elementAt(0); |
||||||
|
return (pre.getNextInstruction() == this && |
||||||
|
pre.getSuccessors().length != 1) ? null : pre; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the predecessors of this instruction. This function mustn't |
||||||
|
* be called before resolveSuccessors is executed for all |
||||||
|
* InstructionHeaders. |
||||||
|
* @return Vector of predecessors. |
||||||
|
*/ |
||||||
|
public Vector getPredecessors() { |
||||||
|
return predecessors; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resolve the successors and predecessors and build a doubly |
||||||
|
* linked list. |
||||||
|
* @param instHeaders an array of the InstructionHeaders, indexed |
||||||
|
* by addresses. |
||||||
|
*/ |
||||||
|
public void resolveSuccessors(InstructionHeader[] instHeaders) { |
||||||
|
if (addr+length < instHeaders.length) |
||||||
|
nextInstruction = instHeaders[addr+length]; |
||||||
|
else |
||||||
|
nextInstruction = null; |
||||||
|
successors = new InstructionHeader[succs.length]; |
||||||
|
for (int i=0; i< succs.length; i++) { |
||||||
|
successors[i] = instHeaders[succs[i]]; |
||||||
|
successors[i].predecessors.addElement(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpSource(TabbedPrintWriter writer) |
||||||
|
throws java.io.IOException |
||||||
|
{ |
||||||
|
if (writer.verbosity > 5) { |
||||||
|
writer.println("<"+addr + " - "+(addr+length-1)+">"); |
||||||
|
writer.tab(); |
||||||
|
} |
||||||
|
if (!hasDirectPredecessor() && addr != 0) |
||||||
|
writer.print("addr_"+addr+": "); |
||||||
|
|
||||||
|
if (switchType == MyType.tBoolean) { |
||||||
|
|
||||||
|
writer.println("if ("+instr.toString()+") goto addr_"+succs[1]); |
||||||
|
if (succs[0] != addr + length) |
||||||
|
writer.println("goto addr_"+succs[0]); |
||||||
|
|
||||||
|
} else if (switchType == MyType.tVoid) { |
||||||
|
|
||||||
|
if (instr.getType() != MyType.tVoid) |
||||||
|
writer.print("push "); |
||||||
|
writer.println(instr.toString()+";"); |
||||||
|
if (succs.length > 0 && succs[0] != addr + length) |
||||||
|
writer.println("goto addr_"+succs[0]); |
||||||
|
|
||||||
|
} else { |
||||||
|
writer.println("switch ("+instr.toString()+") {"); |
||||||
|
writer.tab(); |
||||||
|
writer.untab(); |
||||||
|
} |
||||||
|
if (writer.verbosity > 5) |
||||||
|
writer.untab(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method replaces multiple InstructionHeaders by a single one. |
||||||
|
* The next count Instructions must be unique. |
||||||
|
* @param count the number of InstructionHeaders that should be replaced. |
||||||
|
* @param instr the new instruction; this should be equivalent to the |
||||||
|
* old <em>count</em instructions. |
||||||
|
*/ |
||||||
|
public void combine(int count, Instruction newInstr) { |
||||||
|
InstructionHeader last = this; |
||||||
|
this.instr = newInstr; |
||||||
|
for (int i=1; i < count; i++) { |
||||||
|
last = last.getSuccessors()[0]; |
||||||
|
length += last.length; |
||||||
|
} |
||||||
|
switchType = last.switchType; |
||||||
|
cases = last.cases; |
||||||
|
succs = last.succs; |
||||||
|
successors = last.successors; |
||||||
|
nextInstruction = last.nextInstruction; |
||||||
|
for (int i=0; i< successors.length; i++) { |
||||||
|
successors[i].predecessors.removeElement(last); |
||||||
|
successors[i].predecessors.addElement(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method replaces two conditional InstructionHeaders by a |
||||||
|
* single one. You must make sure that this and the next instruction |
||||||
|
* are both conditional Instructions and the destinations matches. |
||||||
|
* |
||||||
|
* @param newCondition the new instruction; this should be equivalent |
||||||
|
* to the old two conditions. |
||||||
|
*/ |
||||||
|
public void combineConditional(Instruction newCondition) { |
||||||
|
nextInstruction.successors[0].predecessors. |
||||||
|
removeElement(nextInstruction); |
||||||
|
nextInstruction.successors[1].predecessors. |
||||||
|
removeElement(nextInstruction); |
||||||
|
instr = newCondition; |
||||||
|
|
||||||
|
succs[0] = nextInstruction.succs[0]; // aid debugging
|
||||||
|
successors[0] = nextInstruction.successors[0]; |
||||||
|
|
||||||
|
if (successors[1] != nextInstruction.successors[1]) { |
||||||
|
succs[1] = nextInstruction.succs[1]; // aid debugging
|
||||||
|
successors[1] = nextInstruction.successors[1]; |
||||||
|
successors[1].predecessors.addElement(this); |
||||||
|
} else { |
||||||
|
successors[0].predecessors.addElement(this); |
||||||
|
} |
||||||
|
|
||||||
|
length += nextInstruction.length; // aid debugging
|
||||||
|
nextInstruction = nextInstruction.nextInstruction; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
package jode; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is an InstructionHeader for an JSR (jump subroutine) opcode. |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class JsrInstructionHeader extends InstructionHeader { |
||||||
|
int dest; |
||||||
|
|
||||||
|
InstructionHeader destination; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an InstructionHeader for a conditional or unconditional |
||||||
|
* Jsr. |
||||||
|
* @param addr The address of this instruction. |
||||||
|
* @param length The length of this instruction. |
||||||
|
* @param dest The destination address of the Jsr. |
||||||
|
* @param instr The undelying Instruction, the type of must be |
||||||
|
* <ul><li> boolean for a conditional Jsr. </li> |
||||||
|
* <li> void for an unconditional Jsr. </li></ul> |
||||||
|
*/ |
||||||
|
public JsrInstructionHeader(int addr, int length, int dest, |
||||||
|
Instruction instr) { |
||||||
|
super(addr,length, instr); |
||||||
|
this.dest = dest; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the successors of this instructions. This function mustn't |
||||||
|
* be called before resolveSuccessors is executed for this |
||||||
|
* InstructionHeaders. |
||||||
|
* @return Array of successors. |
||||||
|
*/ |
||||||
|
public InstructionHeader[] getSuccessors() { |
||||||
|
InstructionHeader[] result = { destination }; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resolve the successors and predecessors and build a doubly |
||||||
|
* linked list. |
||||||
|
* @param instHeaders an array of the InstructionHeaders, indexed |
||||||
|
* by addresses. |
||||||
|
*/ |
||||||
|
public void resolveSuccessors(InstructionHeader[] instHeaders) { |
||||||
|
nextInstruction = instHeaders[addr+length]; |
||||||
|
destination = instHeaders[dest]; |
||||||
|
destination.predecessors.addElement(this); |
||||||
|
/* Ret.successors.addElement(nextInstruction); XXX */ |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "Jsr " + dest; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
public class LocalLoadOperator extends ConstOperator |
||||||
|
implements LocalVarOperator { |
||||||
|
int slot; |
||||||
|
LocalInfo local; |
||||||
|
|
||||||
|
public LocalLoadOperator(Type type, int slot) { |
||||||
|
super(type, ""); |
||||||
|
this.slot = slot; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isRead() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isWrite() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLocalInfo(LocalInfo local) { |
||||||
|
local.setType(type); |
||||||
|
this.local = local; |
||||||
|
} |
||||||
|
|
||||||
|
public LocalInfo getLocalInfo() { |
||||||
|
return local; |
||||||
|
} |
||||||
|
|
||||||
|
public Type getType() { |
||||||
|
System.err.println("LocalLoad.getType of "+local.getName()+": "+local.getType()); |
||||||
|
return local.getType(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean setType(Type type) { |
||||||
|
System.err.println("LocalLoad.setType of "+local.getName()+": "+local.getType()); |
||||||
|
return super.setType(local.setType(type)); |
||||||
|
} |
||||||
|
|
||||||
|
public int getSlot() { |
||||||
|
return slot; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString(String[] operands) { |
||||||
|
return local.getName().toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean equals(Object o) { |
||||||
|
return (o instanceof LocalLoadOperator && |
||||||
|
((LocalLoadOperator) o).slot == slot); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,19 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
public class LocalPostFixOperator extends NoArgOperator { |
||||||
|
IIncOperator iinc; |
||||||
|
|
||||||
|
public LocalPostFixOperator(Type type, int op, IIncOperator iinc) { |
||||||
|
super(type, op); |
||||||
|
this.iinc = iinc; |
||||||
|
} |
||||||
|
|
||||||
|
public int getPriority() { |
||||||
|
return 800; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString(String[] operands) { |
||||||
|
return iinc.getLocalInfo().getName() + getOperatorString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
public class LocalStoreOperator extends StoreInstruction |
||||||
|
implements LocalVarOperator { |
||||||
|
int slot; |
||||||
|
LocalInfo local; |
||||||
|
|
||||||
|
public LocalStoreOperator(Type lvalueType, int slot, int operator) { |
||||||
|
super(lvalueType, operator); |
||||||
|
this.slot = slot; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isRead() { |
||||||
|
return operator != ASSIGN_OP; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isWrite() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLocalInfo(LocalInfo local) { |
||||||
|
local.setType(lvalueType); |
||||||
|
this.local = local; |
||||||
|
} |
||||||
|
|
||||||
|
public LocalInfo getLocalInfo() { |
||||||
|
return local; |
||||||
|
} |
||||||
|
|
||||||
|
public Type getLValueType() { |
||||||
|
System.err.println("LocalStore.getType of "+local.getName()+": "+local.getType()); |
||||||
|
return local.getType(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean setLValueType(Type type) { |
||||||
|
System.err.println("LocalStore.setType of "+local.getName()+": "+local.getType()); |
||||||
|
return super.setLValueType |
||||||
|
(local.setType(MyType.tSuperType(type))); |
||||||
|
} |
||||||
|
|
||||||
|
public int getSlot() { |
||||||
|
return slot; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean matches(Operator loadop) { |
||||||
|
return loadop instanceof LocalLoadOperator && |
||||||
|
((LocalLoadOperator)loadop).getLocalInfo().getLocalInfo() |
||||||
|
== local.getLocalInfo(); |
||||||
|
} |
||||||
|
|
||||||
|
public int getLValueOperandCount() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
public int getLValueOperandPriority(int i) { |
||||||
|
/* shouldn't be called */ |
||||||
|
throw new RuntimeException("LocalStoreOperator has no operands"); |
||||||
|
} |
||||||
|
|
||||||
|
public Type getLValueOperandType(int i) { |
||||||
|
/* shouldn't be called */ |
||||||
|
throw new RuntimeException("LocalStoreOperator has no operands"); |
||||||
|
} |
||||||
|
|
||||||
|
public void setLValueOperandType(Type []t) { |
||||||
|
/* shouldn't be called */ |
||||||
|
throw new RuntimeException("LocalStoreOperator has no operands"); |
||||||
|
} |
||||||
|
|
||||||
|
public String getLValueString(String[] operands) { |
||||||
|
return local.getName().toString(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,11 @@ |
|||||||
|
package jode; |
||||||
|
|
||||||
|
public interface LocalVarOperator { |
||||||
|
public boolean isRead(); |
||||||
|
public boolean isWrite(); |
||||||
|
public int getSlot(); |
||||||
|
public LocalInfo getLocalInfo(); |
||||||
|
public void setLocalInfo(LocalInfo li); |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,35 @@ |
|||||||
|
package jode; |
||||||
|
import java.util.Vector; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is the end point of the InstructionHeader list. |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class MethodInstructionHeader extends InstructionHeader { |
||||||
|
/** |
||||||
|
* Create a new InstructionHeader. |
||||||
|
* @param addr The address of this Instruction. |
||||||
|
* @param length The length of this Instruction. |
||||||
|
* @param instr The underlying Instruction. |
||||||
|
*/ |
||||||
|
public MethodInstructionHeader(InstructionHeader[] instr) { |
||||||
|
super(-1,1,null); |
||||||
|
nextInstruction = instr[0]; |
||||||
|
nextInstruction.predecessors.addElement(this); |
||||||
|
for (int addr = 0; addr < instr.length; ) { |
||||||
|
instr[addr].resolveSuccessors(instr); |
||||||
|
if (instr[addr].succs.length == 0) |
||||||
|
predecessors.addElement(instr[addr]); |
||||||
|
addr = instr[addr].getNextAddr(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resolve the successors and predecessors and build a doubly |
||||||
|
* linked list. |
||||||
|
* @param instHeaders an array of the InstructionHeaders, indexed |
||||||
|
* by addresses. |
||||||
|
*/ |
||||||
|
public void resolveSuccessors(InstructionHeader[] instHeaders) { |
||||||
|
} |
||||||
|
} |
@ -1,13 +1,21 @@ |
|||||||
package jode; |
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
public class NopOperator extends Instruction { |
public class NopOperator extends Instruction { |
||||||
public NopOperator(int a, int l) { |
public NopOperator(Type type) { |
||||||
super(a,l); |
super(type); |
||||||
} |
} |
||||||
|
|
||||||
public void dumpSource(TabbedPrintWriter tpw, CodeAnalyzer ca) |
public NopOperator() { |
||||||
throws java.io.IOException |
this(MyType.tVoid); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean equals(Object o) { |
||||||
|
return (o instanceof NopOperator); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() |
||||||
{ |
{ |
||||||
tpw.println("nop;"); |
return "nop"; |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,45 @@ |
|||||||
|
package jode; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is an InstructionHeader for an RET (return from JSR) opcode. |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class RetInstructionHeader extends InstructionHeader { |
||||||
|
int dest; |
||||||
|
boolean conditional; |
||||||
|
|
||||||
|
InstructionHeader destination; |
||||||
|
InstructionHeader[] successors;/*XXX*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an InstructionHeader for a conditional or unconditional |
||||||
|
* Ret. |
||||||
|
* @param addr The address of this instruction. |
||||||
|
* @param length The length of this instruction. |
||||||
|
* @param instr The underlying Instruction of int type (ret addr). |
||||||
|
*/ |
||||||
|
public RetInstructionHeader(int addr, int length, Instruction instr) { |
||||||
|
super(addr, length, instr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the successors of this instructions. This function mustn't |
||||||
|
* be called before resolveSuccessors is executed for this |
||||||
|
* InstructionHeaders. |
||||||
|
* @return Array of successors. |
||||||
|
*/ |
||||||
|
public InstructionHeader[] getSuccessors() { |
||||||
|
/* XXX */ |
||||||
|
return successors; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resolve the successors and predecessors and build a doubly |
||||||
|
* linked list. |
||||||
|
* @param instHeaders an array of the InstructionHeaders, indexed |
||||||
|
* by addresses. |
||||||
|
*/ |
||||||
|
public void resolveSuccessors(InstructionHeader[] instHeaders) { |
||||||
|
nextInstruction = instHeaders[addr+length]; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
public class StringAddOperator extends SimpleOperator { |
||||||
|
protected Type operandType; |
||||||
|
|
||||||
|
public StringAddOperator() { |
||||||
|
super(MyType.tString, ADD_OP, 2); |
||||||
|
operandTypes[1] = MyType.tUnknown; |
||||||
|
} |
||||||
|
|
||||||
|
public int getPriority() { |
||||||
|
return 610; |
||||||
|
} |
||||||
|
|
||||||
|
public int getOperandPriority(int i) { |
||||||
|
return 610 + i; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean equals(Object o) { |
||||||
|
return (o instanceof StringAddOperator); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString(String[] operands) { |
||||||
|
return operands[0] + getOperatorString() + operands[1]; |
||||||
|
} |
||||||
|
} |
@ -1,13 +1,12 @@ |
|||||||
package jode; |
package jode; |
||||||
|
|
||||||
public class SwapOperator extends Instruction { |
public class SwapOperator extends Instruction { |
||||||
public SwapOperator(int a, int l) { |
public SwapOperator() { |
||||||
super(a,l); |
super(MyType.tError); |
||||||
} |
} |
||||||
|
|
||||||
public void dumpSource(TabbedPrintWriter writer, CodeAnalyzer ca) |
public String toString() |
||||||
throws java.io.IOException |
|
||||||
{ |
{ |
||||||
writer.println("swap;"); |
return "swap"; |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,319 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an object type which isn't fully known. |
||||||
|
* The real object type lies in a range of types between topType |
||||||
|
* and bottomType. <p> |
||||||
|
* |
||||||
|
* For a totally unknown type topType is tObject and bottomType is |
||||||
|
* null. It is always garanteed that topType is an Array or an Object |
||||||
|
* and that bottomType is null or an Array or an Object. <p> |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @date 98/08/06 |
||||||
|
*/ |
||||||
|
public class ClassRangeType extends MyType { |
||||||
|
final Type bottomType; |
||||||
|
final Type topType; |
||||||
|
|
||||||
|
public ClassRangeType(Type bottomType, Type topType) { |
||||||
|
super(103, "-"); |
||||||
|
if (bottomType != null && bottomType.getTypeCode() == 103) |
||||||
|
bottomType = ((ClassRangeType)bottomType).bottomType; |
||||||
|
if (topType != null && topType.getTypeCode() == 103) |
||||||
|
topType = ((ClassRangeType)topType).topType; |
||||||
|
this.bottomType = bottomType; |
||||||
|
this.topType = topType; |
||||||
|
} |
||||||
|
|
||||||
|
public static Type createRangeType(Type bottom, Type top) { |
||||||
|
// TODO: XXX calculate < top, ...> \cap <..., bottom>
|
||||||
|
// e.g. top , bottom result
|
||||||
|
// x , null <x, null>
|
||||||
|
// tUnknown, object <tObject, object>
|
||||||
|
// Fahrrad , Fahrzeug error
|
||||||
|
// Fahrzeug, Fahrrad <Fahrzeug, Fahrrad>
|
||||||
|
// int , Fahrrad error
|
||||||
|
|
||||||
|
/* First the trivial cases |
||||||
|
*/ |
||||||
|
if (top == tError || bottom == tError) |
||||||
|
return tError; |
||||||
|
|
||||||
|
/* This is always okay (right open interval, maybe left open) |
||||||
|
*/ |
||||||
|
if (top == null) |
||||||
|
return new ClassRangeType(bottom,top); |
||||||
|
|
||||||
|
/* <null, object> -> <tObject, object> |
||||||
|
* if bottom is tObject, its okay. |
||||||
|
*/ |
||||||
|
if (bottom == null || bottom == tObject) |
||||||
|
return new ClassRangeType(tObject, top); |
||||||
|
|
||||||
|
/* now bottom != null and top != null */ |
||||||
|
if (bottom.getTypeCode() == 9 && top.getTypeCode() == 9) |
||||||
|
return tArray(createRangeType(bottom.getElementType(), |
||||||
|
top.getElementType())); |
||||||
|
|
||||||
|
if (bottom.getTypeCode() != 10 || top.getTypeCode() != 10) |
||||||
|
return tError; |
||||||
|
|
||||||
|
if (bottom == top) |
||||||
|
return bottom; |
||||||
|
|
||||||
|
ClassDeclaration c1 = new ClassDeclaration(bottom.getClassName()); |
||||||
|
ClassDeclaration c2 = new ClassDeclaration(top.getClassName()); |
||||||
|
|
||||||
|
try { |
||||||
|
if (c1.getClassDefinition(env).superClassOf(env, c2) || |
||||||
|
c1.getClassDefinition(env).implementedBy(env, c2)) |
||||||
|
return new ClassRangeType(bottom, top); |
||||||
|
} catch (ClassNotFound ex) { |
||||||
|
} |
||||||
|
return tError; |
||||||
|
} |
||||||
|
|
||||||
|
public Type getElementType() { |
||||||
|
Type bottom = bottomType != null ? bottomType.getElementType() : null; |
||||||
|
Type top = topType != null ? topType.getElementType() : null; |
||||||
|
return new ClassRangeType(bottom, top); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the specialized type of t1 and t2, e.g |
||||||
|
* null , xx -> xx |
||||||
|
* tObject, object -> object |
||||||
|
* int , short -> short |
||||||
|
* tArray(tObject), tArray(tUnknown) -> tArray(tObject) |
||||||
|
* tArray(tUnknown), tObject -> tArray(tUnknown) |
||||||
|
*/ |
||||||
|
public static Type getSpecializedType(Type t1, Type t2) { |
||||||
|
if (t1 == null || t2 == tError) |
||||||
|
return t2; |
||||||
|
if (t2 == null || t1 == tError) |
||||||
|
return t1; |
||||||
|
|
||||||
|
if (t1.getTypeCode() == 103) { |
||||||
|
t1 = ((ClassRangeType)t1).bottomType; |
||||||
|
if (t1 == null) |
||||||
|
return t2; |
||||||
|
} |
||||||
|
if (t2.getTypeCode() == 103) { |
||||||
|
t2 = ((ClassRangeType)t2).bottomType; |
||||||
|
if (t2 == null) |
||||||
|
return t1; |
||||||
|
} |
||||||
|
|
||||||
|
if (t1 == t2) |
||||||
|
return t1; |
||||||
|
|
||||||
|
if (t1.getTypeCode() <= 4 && t2.getTypeCode() <= 4) { |
||||||
|
if (t1.getTypeCode() < t2.getTypeCode()) |
||||||
|
return t1; |
||||||
|
else |
||||||
|
return t2; |
||||||
|
} |
||||||
|
|
||||||
|
if ((t1.getTypeCode() != 9 && t1.getTypeCode() != 10) || |
||||||
|
(t2.getTypeCode() != 9 && t2.getTypeCode() != 10)) |
||||||
|
return tError; |
||||||
|
|
||||||
|
if (t1 == MyType.tObject) |
||||||
|
return t2; |
||||||
|
if (t2 == MyType.tObject) |
||||||
|
return t1; |
||||||
|
|
||||||
|
if (t1.getTypeCode() == 9 && t2.getTypeCode() == 9) |
||||||
|
return tArray(getSpecializedType(t1.getElementType(), |
||||||
|
t2.getElementType())); |
||||||
|
|
||||||
|
if (t1.getTypeCode() != 10 && t2.getTypeCode() != 10) |
||||||
|
return tError; |
||||||
|
|
||||||
|
/* Now we have two classes or interfaces. The result should |
||||||
|
* be the object that is the the child of both objects resp |
||||||
|
* implements both interfaces. |
||||||
|
* |
||||||
|
* I currently only handle the simple case where one of the |
||||||
|
* two objects implements the other or is a child of it. |
||||||
|
* |
||||||
|
* Forget the following setences, java tells us if the local |
||||||
|
* is an interface or an object. |
||||||
|
* |
||||||
|
* There are really complicated cases that are currently |
||||||
|
* ignored: imaging, c1 and c2 are both disjunct interfaces |
||||||
|
* and there are some object which implements them both. |
||||||
|
* There is no way for us to guess which. |
||||||
|
* |
||||||
|
* What can we do about this? We probably need something more |
||||||
|
* powerful than a simple class range. |
||||||
|
* But maybe this isn't needed at all. How should someone |
||||||
|
* use an object which implements two interfaces in a local |
||||||
|
* variable without casting? The information which object |
||||||
|
* to use must be somewhere in the method. |
||||||
|
* |
||||||
|
* But think of this code fragment: |
||||||
|
* |
||||||
|
* class Foo implements i1, i2 { ... } |
||||||
|
* |
||||||
|
* class Bar { |
||||||
|
* Foo getFoo() { ... } |
||||||
|
* void someFunction() { |
||||||
|
* while ((Foo foo = getFoo()) != null) { |
||||||
|
* foo.interface1Method(); |
||||||
|
* foo.interface2Method(); |
||||||
|
* } |
||||||
|
* } |
||||||
|
* } |
||||||
|
* |
||||||
|
* Since the while condition is moved to the bottom of |
||||||
|
* the loop, the type information of foo is only available |
||||||
|
* <em>after</em> the two interface methods are called. |
||||||
|
* The current code would produce tError. */ |
||||||
|
|
||||||
|
|
||||||
|
ClassDeclaration c1 = new ClassDeclaration(t1.getClassName()); |
||||||
|
ClassDeclaration c2 = new ClassDeclaration(t2.getClassName()); |
||||||
|
|
||||||
|
try { |
||||||
|
if (c1.getClassDefinition(env).superClassOf(env, c2)) |
||||||
|
return t2; |
||||||
|
if (c2.getClassDefinition(env).superClassOf(env, c1)) |
||||||
|
return t1; |
||||||
|
// if (c1.getClassDefinition(env).implementedBy(env, c2))
|
||||||
|
// return t2;
|
||||||
|
// if (c2.getClassDefinition(env).implementedBy(env, c1))
|
||||||
|
// return t1;
|
||||||
|
} catch (ClassNotFound ex) { |
||||||
|
} |
||||||
|
return tError; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the generalized type of t1 and t2, e.g |
||||||
|
* tObject, tString -> tObject |
||||||
|
* int , short -> int |
||||||
|
* tArray(tObject), tArray(tUnknown) -> tArray(tUnknown) |
||||||
|
* tArray(tUnknown), tObject -> tObject |
||||||
|
* tUnknown, tString -> tString !! |
||||||
|
* null , tString -> tString !! |
||||||
|
*/ |
||||||
|
public static Type getGeneralizedType(Type t1, Type t2) { |
||||||
|
if (t1 != null && t1.getTypeCode() == 103) |
||||||
|
t1 = ((ClassRangeType)t1).topType; |
||||||
|
if (t2 != null && t2.getTypeCode() == 103) |
||||||
|
t2 = ((ClassRangeType)t2).topType; |
||||||
|
|
||||||
|
if (t1 == t2 || |
||||||
|
t1 == tError || t2 == null) |
||||||
|
return t1; |
||||||
|
if (t2 == tError || t1 == null) |
||||||
|
return t2; |
||||||
|
|
||||||
|
if (t1.getTypeCode() <= 4 && t2.getTypeCode() <= 4) { |
||||||
|
if (t1.getTypeCode() < t2.getTypeCode()) |
||||||
|
return t2; |
||||||
|
else |
||||||
|
return t1; |
||||||
|
} |
||||||
|
if ((t1.getTypeCode() != 9 && t1.getTypeCode() != 10) || |
||||||
|
(t1.getTypeCode() != 9 && t1.getTypeCode() != 10)) |
||||||
|
return tError; |
||||||
|
|
||||||
|
if (t1 == MyType.tObject) |
||||||
|
return t1; |
||||||
|
if (t2 == MyType.tObject) |
||||||
|
return t2; |
||||||
|
|
||||||
|
if (t1.getTypeCode() == 9 && t2.getTypeCode() == 9) |
||||||
|
return tArray(getGeneralizedType(t1.getElementType(), |
||||||
|
t2.getElementType())); |
||||||
|
|
||||||
|
if (t1.getTypeCode() != 10 && t2.getTypeCode() != 10) |
||||||
|
return tError; |
||||||
|
|
||||||
|
/* This code is not always correct: |
||||||
|
* We don't want a real super type in all cases, but maybe only |
||||||
|
* an interface which both objects implement. Think of this: |
||||||
|
* |
||||||
|
* interface I; |
||||||
|
* class C1 implements I; |
||||||
|
* class C2 implements I; |
||||||
|
* |
||||||
|
* { |
||||||
|
* I var; |
||||||
|
* if (cond) |
||||||
|
* var = getC1(); |
||||||
|
* else |
||||||
|
* var = getC2(); |
||||||
|
* return var.interfaceMethod(); |
||||||
|
* } |
||||||
|
* |
||||||
|
* The current code would first assign the type object to |
||||||
|
* var and then produce a type error when interfaceMethod |
||||||
|
* is called. |
||||||
|
* |
||||||
|
* Now we have proved that we need some better concept for |
||||||
|
* types. (Maybe a set of types for the upper and lower |
||||||
|
* bound) |
||||||
|
*/ |
||||||
|
|
||||||
|
ClassDeclaration c1 = new ClassDeclaration(t1.getClassName()); |
||||||
|
ClassDeclaration c2 = new ClassDeclaration(t2.getClassName()); |
||||||
|
|
||||||
|
try { |
||||||
|
// /* if one of the two types is an interface which
|
||||||
|
// * is implemented by the other type the interface
|
||||||
|
// * is the result.
|
||||||
|
// */
|
||||||
|
// if (c1.getClassDefinition(env).implementedBy(env, c2))
|
||||||
|
// return t1;
|
||||||
|
// if (c2.getClassDefinition(env).implementedBy(env, c1))
|
||||||
|
// return t2;
|
||||||
|
|
||||||
|
ClassDefinition c = c1.getClassDefinition(env); |
||||||
|
while(c != null && !c.superClassOf(env, c2)) { |
||||||
|
c = c.getSuperClass(env).getClassDefinition(env); |
||||||
|
} |
||||||
|
if (c != null) |
||||||
|
return tClass(c.getName()); |
||||||
|
} catch (ClassNotFound ex) { |
||||||
|
} |
||||||
|
return tObject; |
||||||
|
} |
||||||
|
|
||||||
|
public Type getIntersection(ClassRangeType type) |
||||||
|
{ |
||||||
|
Type bottom = getSpecializedType(bottomType, type.bottomType); |
||||||
|
Type top = getGeneralizedType(topType, type.topType); |
||||||
|
|
||||||
|
System.err.println("intersecting "+ this +" and "+ type + |
||||||
|
" to <" + bottom + "-" + top + ">"); |
||||||
|
try { |
||||||
|
throw new AssertError("in:"); |
||||||
|
} catch(AssertError error) { |
||||||
|
error.printStackTrace(); |
||||||
|
} |
||||||
|
return createRangeType(bottom,top); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean intersects(ClassRangeType type) |
||||||
|
{ |
||||||
|
return getIntersection(type) != tError; |
||||||
|
} |
||||||
|
|
||||||
|
public String typeString(String string, boolean flag1, boolean flag2) |
||||||
|
{ |
||||||
|
// if (verbose??)
|
||||||
|
return "<"+bottomType+"-"+topType+">" + string; |
||||||
|
// else
|
||||||
|
// return bottomType.typeString(string, flag1, flag2);
|
||||||
|
} |
||||||
|
|
||||||
|
// public String toString()
|
||||||
|
// {
|
||||||
|
// return "<"+bottomType+"-"+topType+">";
|
||||||
|
// }
|
||||||
|
} |
@ -0,0 +1,199 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Constants; |
||||||
|
import sun.tools.java.Type; |
||||||
|
import sun.tools.java.Identifier; |
||||||
|
import java.util.Hashtable; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is my own type class. It differs from sun.tools.java.Type, in |
||||||
|
* that it maintains a type range. This type range may be implicit or |
||||||
|
* explicit. <p> |
||||||
|
* |
||||||
|
* The type tInt (which is the same as Type.tInt) is a good example |
||||||
|
* for the implicit range <tInt -- tBoolean>. Most other |
||||||
|
* standard types stand for the range consisting only of themselve. |
||||||
|
* The explicit form is the class range type <p> |
||||||
|
* |
||||||
|
* The main operation on a type range is the intersection. To do this |
||||||
|
* on class ranges we need two more operations: specialization and |
||||||
|
* generalization. <p> |
||||||
|
* |
||||||
|
* specialization chooses the startpoint of two intervals which |
||||||
|
* lies in the open range <sp -- null>, where sp is the startpoint |
||||||
|
* of the other interval, or returns tError on failure.<p> |
||||||
|
* |
||||||
|
* generalization chooses the endpoint of two intervals which lies in |
||||||
|
* the open range <null -- ep>, where ep is the endpoint of |
||||||
|
* the other interval, or returns tError on failure.<p> |
||||||
|
*/ |
||||||
|
public class MyType extends Type { |
||||||
|
static Hashtable superclasses = new Hashtable(); |
||||||
|
|
||||||
|
protected static JodeEnvironment env; |
||||||
|
|
||||||
|
public static final Type tStringBuffer = |
||||||
|
Type.tClass(idJavaLangStringBuffer); |
||||||
|
public static final Type tUnknown = new ClassRangeType(null, null); |
||||||
|
public static final Type tUInt = tInt; |
||||||
|
public static final Type tUIndex = tInt; |
||||||
|
public static final Type tUObject = new ClassRangeType(tObject, null); |
||||||
|
|
||||||
|
public static Type tSuperType(Type type) { |
||||||
|
int typeCode = type.getTypeCode(); |
||||||
|
if (typeCode == 9 || typeCode == 10 || typeCode == 103) |
||||||
|
return new ClassRangeType(tObject, type); |
||||||
|
else |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public static Type tSubType(Type type) { |
||||||
|
int typeCode = type.getTypeCode(); |
||||||
|
if (typeCode == 9 || typeCode == 10 || typeCode == 103) |
||||||
|
return new ClassRangeType(type, null); |
||||||
|
else |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public static Type tClassOrArray(Identifier ident) { |
||||||
|
if (ident.toString().charAt(0) == '[') |
||||||
|
return MyType.tType(ident.toString()); |
||||||
|
else |
||||||
|
return MyType.tClass(ident); |
||||||
|
} |
||||||
|
|
||||||
|
public static void setEnvironment(JodeEnvironment e) { |
||||||
|
env = e; |
||||||
|
} |
||||||
|
|
||||||
|
protected MyType(int i, String str) { |
||||||
|
super (i, str); |
||||||
|
} |
||||||
|
|
||||||
|
public int stackSize() |
||||||
|
{ |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
public String typeString(String var, boolean flag1, boolean flag2) |
||||||
|
{ |
||||||
|
String typeStr; |
||||||
|
switch (typeCode) { |
||||||
|
case 100: typeStr="unknown"; break; |
||||||
|
default: |
||||||
|
throw new RuntimeException("Wrong typeCode "+typeCode); |
||||||
|
} |
||||||
|
if (var.length() > 0) |
||||||
|
return typeStr+" "+var; |
||||||
|
return typeStr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Find the intersection of two types |
||||||
|
* @param t1 the first type. |
||||||
|
* @param t2 the second type. |
||||||
|
* @return the intersection, or tError, if a type conflict happens. |
||||||
|
*/ |
||||||
|
public static Type intersection(Type t1, Type t2) { |
||||||
|
System.err.println("intersecting "+ t1 +" and "+ t2); |
||||||
|
/* Trivial cases first. |
||||||
|
*/ |
||||||
|
if (t1 == t2 || t2 == tUnknown) |
||||||
|
return t1; |
||||||
|
if (t1 == tUnknown) |
||||||
|
return t2; |
||||||
|
|
||||||
|
/* This is the integer case |
||||||
|
* tBoolean = 0 ,..., tInt = 4 |
||||||
|
* return the smaller type code. |
||||||
|
*/ |
||||||
|
if (t1.getTypeCode() <= 4 && t2.getTypeCode() <= 4) { |
||||||
|
if (t1.getTypeCode() < t2.getTypeCode()) |
||||||
|
return t1; |
||||||
|
else |
||||||
|
return t2; |
||||||
|
} |
||||||
|
|
||||||
|
/* If this is an array or a class convert to class range. |
||||||
|
*/ |
||||||
|
if (t1.getTypeCode() == 9 || t1.getTypeCode() == 10) |
||||||
|
t1 = new ClassRangeType(t1,t1); |
||||||
|
|
||||||
|
if (t2.getTypeCode() == 9 || t2.getTypeCode() == 10) |
||||||
|
t2 = new ClassRangeType(t2,t2); |
||||||
|
|
||||||
|
/* Now it must be a class range type, or we have lost! |
||||||
|
*/ |
||||||
|
if (t1.getTypeCode() != 103 || t2.getTypeCode() != 103) |
||||||
|
throw new AssertError("Types incompatible: "+ |
||||||
|
t1.toString()+","+ t2.toString()); |
||||||
|
// return tError;
|
||||||
|
|
||||||
|
return ((ClassRangeType)t1).getIntersection((ClassRangeType)t2); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @deprecated renamed to intersection. |
||||||
|
*/ |
||||||
|
public static Type commonType(Type t1, Type t2) { |
||||||
|
return intersection(t1, t2); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if t1 is in <unknown -- t2&rt;. |
||||||
|
* @return true if t1 is a more specific type than t2, e.g. |
||||||
|
* if t2 is a superclass of t1 |
||||||
|
* @deprecated think about it, you don't need it! (I think) |
||||||
|
* this code is probably broken so don't use it! |
||||||
|
*/ |
||||||
|
public static boolean isOfType(Type t1, Type t2) { |
||||||
|
if ((t1 == t2 || t2 == tUnknown) && t1 != tError) |
||||||
|
return true; |
||||||
|
|
||||||
|
switch (t1.getTypeCode()) { |
||||||
|
case 0: /* boolean*/ |
||||||
|
case 1: /* byte */ |
||||||
|
case 2: /* char */ |
||||||
|
case 3: /* short */ |
||||||
|
case 4: /* int */ |
||||||
|
|
||||||
|
/* JavaC thinks, that this is okay. */ |
||||||
|
if (t2.getTypeCode() >= 0 && t2.getTypeCode() <=4) |
||||||
|
return true; |
||||||
|
|
||||||
|
// /* fallthrough */
|
||||||
|
// case 104: /* unknown index */
|
||||||
|
// if (t2 == tUInt)
|
||||||
|
// return true;
|
||||||
|
break; |
||||||
|
|
||||||
|
case 5: /* long */ |
||||||
|
case 6: /* float */ |
||||||
|
case 7: /* double */ |
||||||
|
case 8: /* null? */ |
||||||
|
case 11: /* void */ |
||||||
|
case 12: /* method */ |
||||||
|
case 13: /* error */ |
||||||
|
// case 101: /* unknown int */
|
||||||
|
/* This are only to themself compatible */ |
||||||
|
break; |
||||||
|
|
||||||
|
case 9: /* array */ |
||||||
|
case 10: /* class */ |
||||||
|
t1 = new ClassRangeType(t1, null); |
||||||
|
/* fall through */ |
||||||
|
case 103: /* class range type */ |
||||||
|
if (t2.getTypeCode() == 103) |
||||||
|
return ((ClassRangeType)t1).intersects((ClassRangeType)t2); |
||||||
|
|
||||||
|
if (t2.getTypeCode() == 9 || t2.getTypeCode() == 10) |
||||||
|
return ((ClassRangeType)t1). |
||||||
|
intersects(new ClassRangeType(t2, null)); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
throw new AssertError("Wrong typeCode "+t1.getTypeCode()); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package jode; |
||||||
|
import sun.tools.java.Type; |
||||||
|
|
||||||
|
public class UnknownSuperType extends MyType { |
||||||
|
Type elemType; |
||||||
|
|
||||||
|
public UnknownSuperType(Type type) { |
||||||
|
super(103, "<"); |
||||||
|
elemType = type; |
||||||
|
} |
||||||
|
|
||||||
|
public Type getElementType() |
||||||
|
{ |
||||||
|
return elemType; |
||||||
|
} |
||||||
|
|
||||||
|
public String typeString(String string, boolean flag1, boolean flag2) |
||||||
|
{ |
||||||
|
return "<superclass of "+ |
||||||
|
String.valueOf(elemType.typeString(string, flag1, flag2))+">"; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue