Mirror of the JODE repository
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
jode/jode/jode/jvm/CodeVerifier.java.in

1027 lines
33 KiB

/* CodeVerifier Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.jvm;
import jode.AssertError;
import jode.GlobalOptions;
import jode.bytecode.BytecodeInfo;
import jode.bytecode.ClassInfo;
import jode.bytecode.Handler;
import jode.bytecode.Instruction;
import jode.bytecode.MethodInfo;
import jode.bytecode.Opcodes;
import jode.bytecode.Reference;
import jode.bytecode.TypeSignature;
import java.util.BitSet;
import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.HashSet;
import @COLLECTIONS@.List;
import @COLLECTIONS@.ArrayList;
public class CodeVerifier implements Opcodes {
ClassInfo ci;
MethodInfo mi;
BytecodeInfo bi;
String methodType;
String returnType;
List jsrTargetList = new ArrayList();
/**
* We need some more types, than mentioned in jvm:
* "ZBCSIFJDL[" are the normal types.
* "?" stands for type error
* "*" stands for unknown type
* "L;" stands for unknown Object type.
* "N...;" stands for new uninitialized type.
* "n...;" stands for new uninitialized type that may call super.
* "R...;" stands for return address type.
* "2" stands for second half of a two word type.
*/
/**
* JLS 4.9.6: Verifying code that contains a finally clause:
* - Each instruction keeps track of the list of jsr targets.
* - For each instruction and each jsr needed to reach that instruction
* a bit vector is maintained of all local vars accessed or modified.
*/
class VerifyInfo implements Cloneable {
String[] stack = new String[bi.getMaxStack()];
String[] locals = new String[bi.getMaxLocals()];
Instruction[] jsrTargets = null;
BitSet[] jsrLocals = null;
int stackHeight = 0;
int maxHeight = 0;
/* If this is a jsr target, this field contains the single
* allowed ret instruction.
*/
Instruction retInstr = null;
public Object clone() {
try {
VerifyInfo result = (VerifyInfo) super.clone();
result.stack = (String[]) stack.clone();
result.locals = (String[]) locals.clone();
return result;
} catch(CloneNotSupportedException ex) {
throw new AssertError("Clone not supported?");
}
}
public final void reserve(int count) throws VerifyException {
if (stackHeight + count > maxHeight) {
maxHeight = stackHeight + count;
if (maxHeight > stack.length)
throw new VerifyException("stack overflow");
}
}
public final void need(int count) throws VerifyException {
if (stackHeight < count)
throw new VerifyException("stack underflow");
}
public final void push(String type) throws VerifyException {
reserve(1);
stack[stackHeight++] = type;
}
public final String pop() throws VerifyException {
need(1);
return stack[--stackHeight];
}
public String toString() {
StringBuffer result = new StringBuffer("locals:[");
String comma = "";
for (int i=0; i<locals.length; i++) {
result.append(comma).append(i).append(':');
result.append(locals[i]);
comma = ",";
}
result.append("], stack:[");
comma = "";
for (int i=0; i<stackHeight; i++) {
result.append(comma).append(stack[i]);
comma = ",";
}
if (jsrTargets != null) {
result.append("], jsrs:[");
comma = "";
for (int i=0; i<jsrTargets.length; i++) {
result.append(comma).append(jsrTargets[i])
.append(jsrLocals[i]);
comma = ",";
}
}
return result.append("]").toString();
}
}
public CodeVerifier(ClassInfo ci, MethodInfo mi, BytecodeInfo bi) {
this.ci = ci;
this.mi = mi;
this.bi = bi;
this.methodType = mi.getType();
this.returnType = TypeSignature.getReturnType(methodType);
}
public VerifyInfo initInfo() {
VerifyInfo info = new VerifyInfo();
int pos = 1;
int slot = 0;
if (!mi.isStatic()) {
String clazzName = ci.getName().replace('.','/');
if (mi.getName().equals("<init>"))
info.locals[slot++] = "n"+ clazzName+";";
else
info.locals[slot++] = "L"+ clazzName+";";
}
while (methodType.charAt(pos) != ')') {
int start = pos;
pos = TypeSignature.skipType(methodType, pos);
String paramType = methodType.substring(start, pos);
info.locals[slot++] = paramType;
if (TypeSignature.getTypeSize(paramType) == 2)
info.locals[slot++] = "2";
}
while (slot < bi.getMaxLocals())
info.locals[slot++] = "?";
return info;
}
/**
* @return true, iff t1 is castable to t2 by a widening cast.
*/
public boolean isOfType(String t1, String t2) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_VERIFIER) != 0)
GlobalOptions.err.println("isOfType("+t1+","+t2+")");
if (t1.equals(t2))
return true;
char c1 = t1.charAt(0);
char c2 = t2.charAt(0);
if (c2 == '*')
return true;
if ("ZBCSI".indexOf(c1) >= 0 && "ZBCSI".indexOf(c2) >= 0)
return true;
if (t1.equals("L;"))
return ("L[".indexOf(c2) >= 0);
if (t2.equals("L;"))
return ("L[nN".indexOf(c1) >= 0);
if (c1 == '[') {
if (c2 == '[') {
String e1 = t1.substring(1);
String e2 = t2.substring(1);
char ec1 = e1.charAt(0);
char ec2 = e2.charAt(0);
if (ec2 == '*')
return true;
/* Note that short[] is not compatible to int[],
* therefore this extra check.
*/
if ("L[".indexOf(ec1) >= 0 && "L[".indexOf(ec2) >= 0)
return isOfType(e1, e2);
} else
return t2.equals("Ljava/lang/Object;");
}
if (c1 == 'L' && c2 == 'L') {
ClassInfo wantedType = TypeSignature.getClassInfo(t2);
return wantedType.isInterface()
|| wantedType.superClassOf(TypeSignature.getClassInfo(t1));
}
return false;
}
/**
* @return true, iff t1 is castable to t2 by a widening cast.
*/
public String mergeType(String t1, String t2) {
if (t1.equals(t2))
return t1;
char c1 = t1.charAt(0);
char c2 = t2.charAt(0);
if (c1 == '*')
return t2;
if (c2 == '*')
return t1;
if ("ZBCSI".indexOf(c1) >= 0 && "ZBCSI".indexOf(c2) >= 0)
return t1;
if (t1.equals("L;"))
return ("L[nN".indexOf(c2) >= 0) ? t2 : "?";
if (t2.equals("L;"))
return ("L[nN".indexOf(c1) >= 0) ? t1 : "?";
if (c1 == '[') {
if (c2 == '[') {
String e1 = t1.substring(1);
String e2 = t2.substring(1);
char ec1 = e1.charAt(0);
char ec2 = e2.charAt(0);
if (ec1 == '*')
return t2;
if (ec2 == '*')
return t1;
/* Note that short[] is not compatible to int[],
* therefore this extra check.
*/
if ("L[".indexOf(ec1) >= 0 && "L[".indexOf(ec2) >= 0)
return ("["+mergeType(e1, e2)).intern();
return "Ljava/lang/Object;";
} else if (c2 == 'L')
return "Ljava/lang/Object;";
return "?";
}
if (c1 == 'L') {
if (c2 == '[')
return "Ljava/lang/Object;";
if (c2 == 'L') {
ClassInfo clazz1 = TypeSignature.getClassInfo(t1);
ClassInfo clazz2 = TypeSignature.getClassInfo(t2);
if (clazz1.superClassOf(clazz2))
return t1;
if (clazz2.superClassOf(clazz1))
return t2;
do {
clazz1 = clazz1.getSuperclass();
} while (!clazz1.superClassOf(clazz2));
return ("L"+clazz1.getName().replace('.', '/')+";").intern();
}
}
return "?";
}
public boolean mergeInfo(Instruction instr, VerifyInfo info)
throws VerifyException {
if (instr.getTmpInfo() == null) {
instr.setTmpInfo(info);
return true;
}
boolean changed = false;
VerifyInfo oldInfo = (VerifyInfo) instr.getTmpInfo();
if (oldInfo.stackHeight != info.stackHeight)
throw new VerifyException("Stack height differ at: "
+ instr.getDescription());
for (int i=0; i < oldInfo.stackHeight; i++) {
String newType = mergeType(oldInfo.stack[i], info.stack[i]);
if (!newType.equals(oldInfo.stack[i])) {
if (newType == "?")
throw new VerifyException("Type error while merging: "
+ oldInfo.stack[i]
+ " and " + info.stack[i]);
changed = true;
oldInfo.stack[i] = newType;
}
}
for (int i=0; i < bi.getMaxLocals(); i++) {
String newType = mergeType(oldInfo.locals[i], info.locals[i]);
if (!newType.equals(oldInfo.locals[i])) {
changed = true;
oldInfo.locals[i] = newType;
}
}
if (oldInfo.jsrTargets != null) {
int jsrDepth;
if (info.jsrTargets == null)
jsrDepth = 0;
else {
jsrDepth = info.jsrTargets.length;
int infoPtr = 0;
oldInfo_loop:
for (int oldInfoPtr=0;
oldInfoPtr < oldInfo.jsrTargets.length; oldInfoPtr++) {
for (int i=infoPtr; i< jsrDepth; i++) {
if (oldInfo.jsrTargets[oldInfoPtr]
== info.jsrTargets[i]) {
System.arraycopy(info.jsrTargets, i,
info.jsrTargets, infoPtr,
jsrDepth - i);
jsrDepth -= (i - infoPtr);
infoPtr++;
continue oldInfo_loop;
}
}
}
jsrDepth = infoPtr;
}
if (jsrDepth != oldInfo.jsrTargets.length) {
if (jsrDepth == 0)
oldInfo.jsrTargets = null;
else {
oldInfo.jsrTargets = new Instruction[jsrDepth];
System.arraycopy(info.jsrTargets, 0,
oldInfo.jsrTargets, 0, jsrDepth);
}
changed = true;
}
}
return changed;
}
String[] types = { "I", "L", "F", "D", "L;", "B", "C", "S" };
public VerifyInfo modelEffect(Instruction instr, VerifyInfo prevInfo)
throws VerifyException {
int jsrLength =
prevInfo.jsrTargets != null ? prevInfo.jsrTargets.length : 0;
VerifyInfo result = (VerifyInfo) prevInfo.clone();
switch (instr.getOpcode()) {
case opc_nop:
case opc_goto:
break;
case opc_ldc: {
String type;
Object constant = instr.getConstant();
if (constant == null)
type = "L;";
else if (constant instanceof Integer)
type = "I";
else if (constant instanceof Float)
type = "F";
else
type = "Ljava/lang/String;";
result.push(type);
break;
}
case opc_ldc2_w: {
String type;
Object constant = instr.getConstant();
if (constant instanceof Long)
type = "L";
else
type = "D";
result.push(type);
result.push("2");
break;
}
case opc_iload:
case opc_lload:
case opc_fload:
case opc_dload:
case opc_aload: {
if (jsrLength > 0
&& (!result.jsrLocals[jsrLength-1].get(instr.getLocalSlot())
|| ((instr.getOpcode() & 0x1) == 0
&& !result.jsrLocals[jsrLength-1]
.get(instr.getLocalSlot()+1)))) {
result.jsrLocals = (BitSet[]) result.jsrLocals.clone();
result.jsrLocals[jsrLength-1]
= (BitSet) result.jsrLocals[jsrLength-1].clone();
result.jsrLocals[jsrLength-1].set(instr.getLocalSlot());
if ((instr.getOpcode() & 0x1) == 0)
result.jsrLocals[jsrLength-1].set(instr.getLocalSlot() + 1);
}
if ((instr.getOpcode() & 0x1) == 0
&& result.locals[instr.getLocalSlot()+1] != "2")
throw new VerifyException(instr.getDescription());
String type = result.locals[instr.getLocalSlot()];
if (!isOfType(type, types[instr.getOpcode() - opc_iload]))
throw new VerifyException(instr.getDescription());
result.push(type);
if ((instr.getOpcode() & 0x1) == 0)
result.push("2");
break;
}
case opc_iaload: case opc_laload:
case opc_faload: case opc_daload: case opc_aaload:
case opc_baload: case opc_caload: case opc_saload: {
if (!isOfType(result.pop(), "I"))
throw new VerifyException(instr.getDescription());
String arrType = result.pop();
if (!isOfType(arrType,
"["+types[instr.getOpcode() - opc_iaload])
&& (instr.getOpcode() != opc_baload
|| !isOfType(arrType, "[Z")))
throw new VerifyException(instr.getDescription());
String elemType = (arrType.equals("L;")
? types[instr.getOpcode() - opc_iaload]
: TypeSignature.getElementType(arrType));
result.push(elemType);
if (((1 << instr.getOpcode() - opc_iaload) & 0xa) != 0)
result.push("2");
break;
}
case opc_istore: case opc_lstore:
case opc_fstore: case opc_dstore: case opc_astore: {
if (jsrLength > 0
&& (!result.jsrLocals[jsrLength-1].get(instr.getLocalSlot())
|| ((instr.getOpcode() & 0x1) != 0
&& !result.jsrLocals[jsrLength-1]
.get(instr.getLocalSlot()+1)))) {
result.jsrLocals = (BitSet[]) result.jsrLocals.clone();
result.jsrLocals[jsrLength-1]
= (BitSet) result.jsrLocals[jsrLength-1].clone();
result.jsrLocals[jsrLength-1].set(instr.getLocalSlot());
if ((instr.getOpcode() & 0x1) != 0)
result.jsrLocals[jsrLength-1].set(instr.getLocalSlot() + 1);
}
if ((instr.getOpcode() & 0x1) != 0
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
String type = result.pop();
if (instr.getOpcode() != opc_astore || type.charAt(0) != 'R')
if (!isOfType(type, types[instr.getOpcode() - opc_istore]))
throw new VerifyException(instr.getDescription());
result.locals[instr.getLocalSlot()] = type;
if ((instr.getOpcode() & 0x1) != 0)
result.locals[instr.getLocalSlot()+1] = "2";
break;
}
case opc_iastore: case opc_lastore:
case opc_fastore: case opc_dastore: case opc_aastore:
case opc_bastore: case opc_castore: case opc_sastore: {
if (((1 << instr.getOpcode() - opc_iastore) & 0xa) != 0
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
String type = result.pop();
if (!isOfType(result.pop(), "I"))
throw new VerifyException(instr.getDescription());
String arrType = result.pop();
if (!isOfType(arrType, "["+types[instr.getOpcode() - opc_iastore])
&& (instr.getOpcode() != opc_bastore
|| !isOfType(arrType, "[Z")))
throw new VerifyException(instr.getDescription());
String elemType = instr.getOpcode() >= opc_bastore ? "I"
: types[instr.getOpcode() - opc_iastore];
if (!isOfType(type, elemType))
throw new VerifyException(instr.getDescription());
break;
}
case opc_pop: case opc_pop2: {
int count = instr.getOpcode() - (opc_pop-1);
result.need(count);
result.stackHeight -= count;
break;
}
case opc_dup: case opc_dup_x1: case opc_dup_x2: {
int depth = instr.getOpcode() - opc_dup;
result.reserve(1);
result.need(depth+1);
if (result.stack[result.stackHeight-1] == "2")
throw new VerifyException(instr.getDescription());
int stackdepth = result.stackHeight - (depth + 1);
if (result.stack[stackdepth] == "2")
throw new VerifyException(instr.getDescription()
+ " on long or double");
for (int i=result.stackHeight; i > stackdepth; i--)
result.stack[i] = result.stack[i-1];
result.stack[stackdepth] = result.stack[result.stackHeight++];
break;
}
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: {
int depth = instr.getOpcode() - opc_dup2;
result.reserve(2);
result.need(depth+2);
if (result.stack[result.stackHeight-2] == "2")
throw new VerifyException(instr.getDescription()
+ " on misaligned long or double");
int stacktop = result.stackHeight;
int stackdepth = stacktop - (depth + 2);
if (result.stack[stackdepth] == "2")
throw new VerifyException(instr.getDescription()
+ " on long or double");
for (int i=stacktop; i > stackdepth; i--)
result.stack[i+1] = result.stack[i-1];
result.stack[stackdepth+1] = result.stack[stacktop+1];
result.stack[stackdepth] = result.stack[stacktop];
result.stackHeight+=2;
break;
}
case opc_swap: {
result.need(2);
if (result.stack[result.stackHeight-2] == "2"
|| result.stack[result.stackHeight-1] == "2")
throw new VerifyException(instr.getDescription()
+ " on misaligned long or double");
String tmp = result.stack[result.stackHeight-1];
result.stack[result.stackHeight-1] =
result.stack[result.stackHeight-2];
result.stack[result.stackHeight-2] = tmp;
break;
}
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
case opc_irem: case opc_lrem: case opc_frem: case opc_drem: {
String type = types[(instr.getOpcode() - opc_iadd) & 3];
if ((instr.getOpcode() & 1) != 0
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (!isOfType(result.pop(), type))
throw new VerifyException(instr.getDescription());
if ((instr.getOpcode() & 1) != 0) {
result.need(2);
if (result.stack[result.stackHeight-1] != "2"
|| !isOfType(result.stack[result.stackHeight-2], type))
throw new VerifyException(instr.getDescription());
} else {
result.need(1);
if (!isOfType(result.stack[result.stackHeight-1], type))
throw new VerifyException(instr.getDescription());
}
break;
}
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: {
String type = types[(instr.getOpcode() - opc_ineg) & 3];
if ((instr.getOpcode() & 1) != 0) {
result.need(2);
if (result.stack[result.stackHeight-1] != "2"
|| !isOfType(result.stack[result.stackHeight-2], type))
throw new VerifyException(instr.getDescription());
} else {
result.need(1);
if (!isOfType(result.stack[result.stackHeight-1], type))
throw new VerifyException(instr.getDescription());
}
break;
}
case opc_ishl: case opc_lshl:
case opc_ishr: case opc_lshr:
case opc_iushr: case opc_lushr:
if (!isOfType(result.pop(), "I"))
throw new VerifyException(instr.getDescription());
if ((instr.getOpcode() & 1) != 0) {
result.need(2);
if (result.stack[result.stackHeight-1] != "2" ||
!isOfType(result.stack[result.stackHeight-2],"L"))
throw new VerifyException(instr.getDescription());
} else {
result.need(1);
if (!isOfType(result.stack[result.stackHeight-1],"I"))
throw new VerifyException(instr.getDescription());
}
break;
case opc_iand: case opc_land:
case opc_ior : case opc_lor :
case opc_ixor: case opc_lxor:
if ((instr.getOpcode() & 1) != 0
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (!isOfType(result.pop(),
types[instr.getOpcode() & 1]))
throw new VerifyException(instr.getDescription());
if ((instr.getOpcode() & 1) != 0) {
result.need(2);
if (result.stack[result.stackHeight-1] != "2"
|| !isOfType(result.stack[result.stackHeight-2],
"L"))
throw new VerifyException(instr.getDescription());
} else {
result.need(1);
if (!isOfType(result.stack[result.stackHeight-1],
"I"))
throw new VerifyException(instr.getDescription());
}
break;
case opc_iinc:
if (!isOfType(result.locals[instr.getLocalSlot()], "I"))
throw new VerifyException(instr.getDescription());
break;
case opc_i2l: case opc_i2f: case opc_i2d:
case opc_l2i: case opc_l2f: case opc_l2d:
case opc_f2i: case opc_f2l: case opc_f2d:
case opc_d2i: case opc_d2l: case opc_d2f: {
int from = (instr.getOpcode()-opc_i2l)/3;
int to = (instr.getOpcode()-opc_i2l)%3;
if (to >= from)
to++;
if ((from & 1) != 0
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (!isOfType(result.pop(), types[from]))
throw new VerifyException(instr.getDescription());
result.push(types[to]);
if ((to & 1) != 0)
result.push("2");
break;
}
case opc_i2b: case opc_i2c: case opc_i2s:
result.need(1);
if (!isOfType(result.stack[result.stackHeight-1], "I"))
throw new VerifyException(instr.getDescription());
break;
case opc_lcmp:
if (result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (result.pop() != "L")
throw new VerifyException(instr.getDescription());
if (result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (result.pop() != "L")
throw new VerifyException(instr.getDescription());
result.push("I");
break;
case opc_dcmpl: case opc_dcmpg:
if (result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (result.pop() != "D")
throw new VerifyException(instr.getDescription());
if (result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (result.pop() != "D")
throw new VerifyException(instr.getDescription());
result.push("I");
break;
case opc_fcmpl: case opc_fcmpg:
if (result.pop() != "F")
throw new VerifyException(instr.getDescription());
if (result.pop() != "F")
throw new VerifyException(instr.getDescription());
result.push("I");
break;
case opc_ifeq: case opc_ifne:
case opc_iflt: case opc_ifge:
case opc_ifgt: case opc_ifle:
case opc_tableswitch:
case opc_lookupswitch:
if (!isOfType(result.pop(), "I"))
throw new VerifyException(instr.getDescription());
break;
case opc_if_icmpeq: case opc_if_icmpne:
case opc_if_icmplt: case opc_if_icmpge:
case opc_if_icmpgt: case opc_if_icmple:
if (!isOfType(result.pop(), "I"))
throw new VerifyException(instr.getDescription());
if (!isOfType(result.pop(), "I"))
throw new VerifyException(instr.getDescription());
break;
case opc_if_acmpeq: case opc_if_acmpne:
if (!isOfType(result.pop(), "L;"))
throw new VerifyException(instr.getDescription());
if (!isOfType(result.pop(), "L;"))
throw new VerifyException(instr.getDescription());
break;
case opc_ifnull: case opc_ifnonnull:
if (!isOfType(result.pop(), "L;"))
throw new VerifyException(instr.getDescription());
break;
case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn: {
if (((1 << instr.getOpcode() - opc_ireturn) & 0xa) != 0
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
String type = result.pop();
if (!isOfType(type, types[instr.getOpcode() - opc_ireturn])
|| !isOfType(type, TypeSignature.getReturnType(methodType)))
throw new VerifyException(instr.getDescription());
break;
}
case opc_jsr: {
Instruction jsrTarget = instr.getSingleSucc();
int index = jsrTargetList.indexOf(jsrTarget);
if (index < 0) {
index = jsrTargetList.size();
jsrTargetList.add(jsrTarget);
}
result.stack[result.stackHeight++] = "R"+index;
result.jsrTargets = new Instruction[jsrLength+1];
result.jsrLocals = new BitSet[jsrLength+1];
if (jsrLength > 0) {
for (int i=0; i< prevInfo.jsrTargets.length; i++)
if (prevInfo.jsrTargets[i] == instr.getSingleSucc())
throw new VerifyException(instr.getDescription()+
" is recursive");
System.arraycopy(prevInfo.jsrTargets, 0,
result.jsrTargets, 0, jsrLength);
System.arraycopy(prevInfo.jsrLocals, 0,
result.jsrLocals, 0, jsrLength);
}
result.jsrTargets[jsrLength] = instr.getSingleSucc();
result.jsrLocals[jsrLength] = new BitSet();
break;
}
case opc_return:
if (!returnType.equals("V"))
throw new VerifyException(instr.getDescription());
break;
case opc_getstatic: {
Reference ref = instr.getReference();
String type = ref.getType();
result.push(type);
if (TypeSignature.getTypeSize(type) == 2)
result.push("2");
break;
}
case opc_getfield: {
Reference ref = instr.getReference();
String classType = ref.getClazz();
if (!isOfType(result.pop(), classType))
throw new VerifyException(instr.getDescription());
String type = ref.getType();
result.push(type);
if (TypeSignature.getTypeSize(type) == 2)
result.push("2");
break;
}
case opc_putstatic: {
Reference ref = instr.getReference();
String type = ref.getType();
if (TypeSignature.getTypeSize(type) == 2
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (!isOfType(result.pop(), type))
throw new VerifyException(instr.getDescription());
break;
}
case opc_putfield: {
Reference ref = instr.getReference();
String type = ref.getType();
if (TypeSignature.getTypeSize(type) == 2
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (!isOfType(result.pop(), type))
throw new VerifyException(instr.getDescription());
String classType = ref.getClazz();
if (!isOfType(result.pop(), classType))
throw new VerifyException(instr.getDescription());
break;
}
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic :
case opc_invokeinterface: {
Reference ref = instr.getReference();
String refmt = ref.getType();
String[] paramTypes = TypeSignature.getParameterTypes(refmt);
for (int i=paramTypes.length - 1; i >= 0; i--) {
if (TypeSignature.getTypeSize(paramTypes[i]) == 2
&& result.pop() != "2")
throw new VerifyException(instr.getDescription());
if (!isOfType(result.pop(), paramTypes[i]))
throw new VerifyException(instr.getDescription());
}
if (ref.getName().equals("<init>")) {
String clazz = result.pop();
String refClazz = ref.getClazz();
if (instr.getOpcode() != opc_invokespecial
|| "Nn".indexOf(clazz.charAt(0)) < 0
|| refClazz.charAt(0) != 'L')
throw new VerifyException(instr.getDescription());
if (!clazz.substring(1).equals(refClazz.substring(1))) {
ClassInfo uci = ClassInfo.forName
(clazz.substring(1, clazz.length()-1)
.replace('/', '.'));
if (uci.getSuperclass()
!= TypeSignature.getClassInfo(refClazz)
|| clazz.charAt(0) == 'N')
throw new VerifyException(instr.getDescription());
}
String newType = "L" + clazz.substring(1);
for (int i=0; i< result.stackHeight; i++)
if (result.stack[i] == clazz)
result.stack[i] = newType;
for (int i=0; i< result.locals.length; i++)
if (result.locals[i] == clazz)
result.locals[i] = newType;
} else if (instr.getOpcode() != opc_invokestatic) {
String classType = ref.getClazz();
if (!isOfType(result.pop(), classType))
throw new VerifyException(instr.getDescription());
}
String type = TypeSignature.getReturnType(refmt);
if (!type.equals("V")) {
result.push(type);
if (TypeSignature.getTypeSize(type) == 2)
result.push("2");
}
break;
}
case opc_new: {
String clName = instr.getClazzType();
result.stack[result.stackHeight++] = "N" + clName.substring(1);
break;
}
case opc_arraylength: {
if (!isOfType(result.pop(), "[*"))
throw new VerifyException(instr.getDescription());
result.push("I");
break;
}
case opc_athrow: {
if (!isOfType(result.pop(), "Ljava/lang/Throwable;"))
throw new VerifyException(instr.getDescription());
break;
}
case opc_checkcast: {
String classType = instr.getClazzType();
if (!isOfType(result.pop(), "L;"))
throw new VerifyException(instr.getDescription());
result.push(classType);
break;
}
case opc_instanceof: {
if (!isOfType(result.pop(), "Ljava/lang/Object;"))
throw new VerifyException(instr.getDescription());
result.push("I");
break;
}
case opc_monitorenter:
case opc_monitorexit:
if (!isOfType(result.pop(), "Ljava/lang/Object;"))
throw new VerifyException(instr.getDescription());
break;
case opc_multianewarray: {
int dimension = instr.getDimensions();
for (int i=dimension - 1; i >= 0; i--)
if (!isOfType(result.pop(), "I"))
throw new VerifyException(instr.getDescription());
String classType = instr.getClazzType();
result.push(classType);
break;
}
default:
throw new AssertError("Invalid opcode "+instr.getOpcode());
}
return result;
}
public void doVerify() throws VerifyException {
HashSet todoSet = new HashSet();
Instruction firstInstr = (Instruction) bi.getInstructions().get(0);
firstInstr.setTmpInfo(initInfo());
todoSet.add(firstInstr);
Handler[] handlers = bi.getExceptionHandlers();
while (!todoSet.isEmpty()) {
Iterator iter = todoSet.iterator();
Instruction instr = (Instruction) iter.next();
iter.remove();
if (!instr.doesAlwaysJump() && instr.getNextByAddr() == null)
throw new VerifyException("Flow can fall off end of method");
VerifyInfo prevInfo = (VerifyInfo) instr.getTmpInfo();
if (instr.getOpcode() == opc_ret) {
String retVarType = prevInfo.locals[instr.getLocalSlot()];
if (prevInfo.jsrTargets == null
|| retVarType.charAt(0) != 'R')
throw new VerifyException(instr.getDescription());
int jsrLength = prevInfo.jsrTargets.length - 1;
Instruction jsrTarget = (Instruction) jsrTargetList.get
(Integer.parseInt(retVarType.substring(1)));
while (jsrTarget != prevInfo.jsrTargets[jsrLength])
if (--jsrLength < 0)
throw new VerifyException(instr.getDescription());
VerifyInfo jsrTargetInfo = (VerifyInfo) jsrTarget.getTmpInfo();
if (jsrTargetInfo.retInstr == null)
jsrTargetInfo.retInstr = instr;
else if (jsrTargetInfo.retInstr != instr)
throw new VerifyException
("JsrTarget has more than one ret: "
+ jsrTarget.getDescription());
Instruction[] nextTargets;
BitSet[] nextLocals;
if (jsrLength > 0) {
nextTargets = new Instruction[jsrLength];
nextLocals = new BitSet[jsrLength];
System.arraycopy(prevInfo.jsrTargets, 0,
nextTargets, 0, jsrLength);
System.arraycopy(prevInfo.jsrLocals, 0,
nextLocals, 0, jsrLength);
} else {
nextTargets = null;
nextLocals = null;
}
for (int i=0; i < jsrTarget.getPreds().length; i++) {
Instruction jsrInstr = jsrTarget.getPreds()[i];
if (jsrInstr.getTmpInfo() != null)
todoSet.add(jsrInstr);
}
} else {
VerifyInfo info = modelEffect(instr, prevInfo);
if (!instr.doesAlwaysJump())
if (mergeInfo(instr.getNextByAddr(), info))
todoSet.add(instr.getNextByAddr());
if (instr.getOpcode() == opc_jsr) {
VerifyInfo targetInfo =
(VerifyInfo) instr.getSingleSucc().getTmpInfo();
if (targetInfo != null && targetInfo.retInstr != null) {
VerifyInfo afterJsrInfo
= (VerifyInfo) prevInfo.clone();
VerifyInfo retInfo
= (VerifyInfo) targetInfo.retInstr.getTmpInfo();
BitSet usedLocals
= retInfo.jsrLocals[retInfo.jsrLocals.length-1];
for (int j = 0; j < bi.getMaxLocals(); j++) {
if (usedLocals.get(j))
afterJsrInfo.locals[j] = retInfo.locals[j];
}
if (mergeInfo(instr.getNextByAddr(), afterJsrInfo))
todoSet.add(instr.getNextByAddr());
}
}
if (instr.getSuccs() != null) {
for (int i=0; i< instr.getSuccs().length; i++) {
if (instr.getSuccs()[i].getAddr() < instr.getAddr()) {
/* This is a backwards branch */
for (int j = 0; j < prevInfo.locals.length; j++) {
if ("Nn".indexOf(prevInfo.locals[j].charAt(0))
>= 0)
throw new VerifyException
("Uninitialized local in back-branch");
}
for (int j = 0; j < prevInfo.stackHeight; j++) {
if ("Nn".indexOf(prevInfo.stack[j].charAt(0))
>= 0)
throw new VerifyException
("Uninitialized stack in back-branch");
}
}
if (mergeInfo(instr.getSuccs()[i],
(VerifyInfo) info.clone()))
todoSet.add(instr.getSuccs()[i]);
}
}
for (int i=0; i<handlers.length; i++) {
if (handlers[i].start.compareTo(instr) <= 0
&& handlers[i].end.compareTo(instr) >= 0) {
for (int j = 0; j < prevInfo.locals.length; j++) {
if ("Nn".indexOf(prevInfo.locals[j].charAt(0))
>= 0)
throw new VerifyException
("Uninitialized local in try block");
}
VerifyInfo excInfo = (VerifyInfo) prevInfo.clone();
excInfo.stackHeight = 1;
if (handlers[i].type != null)
excInfo.stack[0] =
"L" + handlers[i].type.replace('.', '/') + ";";
else
excInfo.stack[0] = "Ljava/lang/Throwable;";
if (mergeInfo(handlers[i].catcher, excInfo))
todoSet.add(handlers[i].catcher);
}
}
}
}
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_VERIFIER) != 0) {
for (Iterator i = bi.getInstructions().iterator(); i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
VerifyInfo info = (VerifyInfo) instr.getTmpInfo();
if (info != null)
GlobalOptions.err.println(info.toString());
GlobalOptions.err.println(instr.getDescription());
}
}
for (Iterator i = bi.getInstructions().iterator(); i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
instr.setTmpInfo(null);
}
}
public void verify() throws VerifyException {
try {
doVerify();
} catch (VerifyException ex) {
for (Iterator i = bi.getInstructions().iterator(); i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
VerifyInfo info = (VerifyInfo) instr.getTmpInfo();
if (info != null)
GlobalOptions.err.println(info.toString());
GlobalOptions.err.println(instr.getDescription());
instr.setTmpInfo(null);
}
throw ex;
}
}
}