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; |
||||
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 { |
||||
static int serialnr = 0; |
||||
Identifier name; |
||||
Type type; |
||||
private static int serialnr = 0; |
||||
private Identifier name; |
||||
private Type type; |
||||
private LocalInfo shadow; |
||||
|
||||
public LocalInfo() { |
||||
name = Identifier.lookup("__"+serialnr); |
||||
type = Type.tUnknown; |
||||
/** |
||||
* Create a new local info. The name will be an identifier |
||||
* 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++; |
||||
} |
||||
|
||||
/** |
||||
* 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; |
||||
import sun.tools.java.Type; |
||||
|
||||
public class CompareToIntOperator extends BinaryOperator { |
||||
public CompareToIntOperator(int addr, int length, Type type, int op) { |
||||
super(addr,length, Type.tInt, op); |
||||
operandType = type; |
||||
public class CompareToIntOperator extends SimpleOperator { |
||||
public CompareToIntOperator(Type type, int lessGreater) { |
||||
super(Type.tInt, 0, 2); |
||||
operandTypes[0] = operandTypes[1] = type; |
||||
} |
||||
|
||||
public int getPriority() { |
||||
switch (getOperator()) { |
||||
case 25: |
||||
case 26: |
||||
return 500; |
||||
case 27: |
||||
case 28: |
||||
case 29: |
||||
case 30: |
||||
return 550; |
||||
} |
||||
throw new RuntimeException("Illegal operator"); |
||||
return 499; |
||||
} |
||||
|
||||
public String toString(CodeAnalyzer ca, String[] operands) { |
||||
return operands[0] + " "+opString[operator]+" "+operands[1]; |
||||
public int getOperandPriority(int i) { |
||||
return 550; |
||||
} |
||||
|
||||
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(String[] operands) { |
||||
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; |
||||
import sun.tools.java.Type; |
||||
|
||||
public abstract class Instruction { |
||||
int addr,length; |
||||
protected Type type; |
||||
|
||||
Instruction(int a, int l) { |
||||
addr = a; |
||||
length = l; |
||||
Instruction(Type type) { |
||||
this.type = type; |
||||
} |
||||
|
||||
public int getAddr() { |
||||
return addr; |
||||
public Type getType() { |
||||
return type; |
||||
} |
||||
|
||||
public void setAddr(int addr) { |
||||
this.addr = addr; |
||||
public Instruction simplify() { |
||||
return this; |
||||
} |
||||
|
||||
public int getLength() { |
||||
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; |
||||
public abstract String toString(); |
||||
} |
||||
|
@ -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; |
||||
import sun.tools.java.Type; |
||||
|
||||
public class NopOperator extends Instruction { |
||||
public NopOperator(int a, int l) { |
||||
super(a,l); |
||||
public NopOperator(Type type) { |
||||
super(type); |
||||
} |
||||
|
||||
public void dumpSource(TabbedPrintWriter tpw, CodeAnalyzer ca) |
||||
throws java.io.IOException |
||||
public NopOperator() { |
||||
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; |
||||
|
||||
public class SwapOperator extends Instruction { |
||||
public SwapOperator(int a, int l) { |
||||
super(a,l); |
||||
public SwapOperator() { |
||||
super(MyType.tError); |
||||
} |
||||
|
||||
public void dumpSource(TabbedPrintWriter writer, CodeAnalyzer ca) |
||||
throws java.io.IOException |
||||
public String toString() |
||||
{ |
||||
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