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.
1137 lines
36 KiB
1137 lines
36 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;
|
|
|
|
static Type tNull = Type.tType("0");
|
|
static Type tInt = Type.tType("I");
|
|
static Type tLong = Type.tType("J");
|
|
static Type tFloat = Type.tType("F");
|
|
static Type tDouble = Type.tType("D");
|
|
static Type tString = Type.tType("Ljava/lang/String;");
|
|
static Type tNone = Type.tType("?");
|
|
static Type tSecondPart = new Type("2");
|
|
static Type tObject = new Type("Ljava/lang/Object;");
|
|
|
|
|
|
/**
|
|
* We need some more types, than mentioned in jvm.
|
|
*/
|
|
private static class Type {
|
|
/* "ZBCSIFJD" are the normal primitive types.
|
|
* "L...;" is normal class type.
|
|
* "[..." is normal array type
|
|
* "?" stands for type error
|
|
* "N...;" stands for new uninitialized type.
|
|
* "0" stands for null type.
|
|
* "R" stands for return address type.
|
|
* "2" stands for second half of a two word type.
|
|
*/
|
|
private String typeSig;
|
|
|
|
/**
|
|
* The dependant instruction. This has two usages:
|
|
* <dl> <dt>"N...;"</dt>
|
|
* <dd> The new instruction, or null if this is the this param
|
|
* of <init>.</dd>
|
|
* <dt>"R"</dt> <dd>The <i>target</i> of the jsr.
|
|
*/
|
|
private Instruction instr;
|
|
|
|
public Type(String typeSig) {
|
|
this.typeSig = typeSig;
|
|
}
|
|
|
|
public Type(String typeSig, Instruction instr) {
|
|
this.typeSig = typeSig;
|
|
this.instr = instr;
|
|
}
|
|
|
|
public static Type tType(String typeSig) {
|
|
// unify them?
|
|
return new Type(typeSig);
|
|
}
|
|
|
|
public static Type tType(String typeSig, Instruction instr) {
|
|
// unify them?
|
|
return new Type(typeSig, instr);
|
|
}
|
|
|
|
public String getTypeSig() {
|
|
return typeSig;
|
|
}
|
|
|
|
public Instruction getInstruction() {
|
|
return instr;
|
|
}
|
|
|
|
/**
|
|
* @param t2 the type signature of the type to check for.
|
|
* This may be one of the special signatures:
|
|
* <dl><dt>"[*"<dt><dd>array of something</dd>
|
|
* <dt>"+"</dt><dd>(uninitialized) object/returnvalue type</dd>
|
|
* <dt>"2", "R"</dt> <dd>as the typeSig parameter </dd>
|
|
* </dl>
|
|
* @return true, iff this is castable to t2 by a
|
|
* widening cast. */
|
|
public boolean isOfType(String destSig) {
|
|
String thisSig = typeSig;
|
|
if ((GlobalOptions.debuggingFlags
|
|
& GlobalOptions.DEBUG_VERIFIER) != 0)
|
|
GlobalOptions.err.println("isOfType("+thisSig+","+destSig+")");
|
|
if (thisSig.equals(destSig))
|
|
return true;
|
|
|
|
char c1 = thisSig.charAt(0);
|
|
char c2 = destSig.charAt(0);
|
|
switch (c2) {
|
|
case 'Z': case 'B': case 'C': case 'S': case 'I':
|
|
/* integer type */
|
|
return ("ZBCSI".indexOf(c1) >= 0);
|
|
case '+':
|
|
return ("L[nNR0".indexOf(c1) >= 0);
|
|
|
|
case '[':
|
|
if (c1 == '0')
|
|
return true;
|
|
while (c1 == '[' && c2 == '[') {
|
|
thisSig = thisSig.substring(1);
|
|
destSig = destSig.substring(1);
|
|
c1 = thisSig.charAt(0);
|
|
c2 = destSig.charAt(0);
|
|
}
|
|
|
|
if (c2 == '*')
|
|
/* destType is array of unknowns */
|
|
return true;
|
|
/* Note that short[] is only compatible to short[],
|
|
* therefore we only need to handle Object types specially.
|
|
*/
|
|
|
|
if (c2 != 'L')
|
|
return false;
|
|
/* fall through*/
|
|
case 'L':
|
|
if (c1 == '0')
|
|
return true;
|
|
if ("L[".indexOf(c1) < 0)
|
|
return false;
|
|
|
|
ClassInfo wantedType = TypeSignature.getClassInfo(destSig);
|
|
if (wantedType.isInterface()
|
|
|| wantedType == ClassInfo.javaLangObject)
|
|
return true;
|
|
if (c1 == 'L')
|
|
return wantedType.superClassOf(TypeSignature
|
|
.getClassInfo(thisSig));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return The common super type of this and type2.
|
|
*/
|
|
public Type mergeType(Type type2) {
|
|
String sig1 = typeSig;
|
|
String sig2 = type2.typeSig;
|
|
|
|
if (this.equals(type2))
|
|
return this;
|
|
|
|
char c1 = sig1.charAt(0);
|
|
char c2 = sig2.charAt(0);
|
|
if (c1 == '*')
|
|
return type2;
|
|
if (c2 == '*')
|
|
return this;
|
|
if ("ZBCSI".indexOf(c1) >= 0 && "ZBCSI".indexOf(c2) >= 0)
|
|
return this;
|
|
|
|
if (c1 == '0')
|
|
return ("L[0".indexOf(c2) >= 0) ? type2 : tNone;
|
|
if (c2 == '0')
|
|
return ("L[".indexOf(c1) >= 0) ? this : tNone;
|
|
|
|
|
|
int dimensions = 0;
|
|
/* Note that short[] is only compatible to short[],
|
|
* therefore we make the array handling after the primitive
|
|
* type handling. Also note that we don't allow arrays of
|
|
* special types.
|
|
*/
|
|
while (c1 == '[' && c2 == '[') {
|
|
sig1 = sig1.substring(1);
|
|
sig2 = sig2.substring(1);
|
|
c1 = sig1.charAt(0);
|
|
c2 = sig2.charAt(0);
|
|
dimensions++;
|
|
}
|
|
|
|
// One of them is array now, the other is an object,
|
|
// the common super is tObject
|
|
if ((c1 == '[' && c2 == 'L')
|
|
|| (c1 == 'L' && c2 == '[')) {
|
|
if (dimensions == 0)
|
|
return tObject;
|
|
StringBuffer result = new StringBuffer(dimensions + 18);
|
|
for (int i=0; i< dimensions; i++)
|
|
result.append("[");
|
|
result.append("Ljava/lang/Object;");
|
|
return tType(result.toString());
|
|
}
|
|
|
|
if (c1 == 'L' && c2 == 'L') {
|
|
ClassInfo clazz1 = TypeSignature.getClassInfo(sig1);
|
|
ClassInfo clazz2 = TypeSignature.getClassInfo(sig2);
|
|
if (clazz1.superClassOf(clazz2))
|
|
return this;
|
|
if (clazz2.superClassOf(clazz1))
|
|
return type2;
|
|
do {
|
|
clazz1 = clazz1.getSuperclass();
|
|
} while (!clazz1.superClassOf(clazz2));
|
|
StringBuffer result = new StringBuffer
|
|
(dimensions + clazz1.getName().length() + 2);
|
|
for (int i=0; i< dimensions; i++)
|
|
result.append("[");
|
|
result.append("L")
|
|
.append(clazz1.getName().replace('.', '/')).append(";");
|
|
return tType(result.toString());
|
|
}
|
|
|
|
// Both were arrays, but of different primitive types. The
|
|
// common super is tObject with one dimension less.
|
|
if (dimensions > 0) {
|
|
if (dimensions == 1)
|
|
return tObject;
|
|
StringBuffer result = new StringBuffer(dimensions + 18);
|
|
for (int i=0; i< dimensions; i++)
|
|
result.append("[");
|
|
result.append("Ljava/lang/Object;");
|
|
return tType(result.toString());
|
|
}
|
|
return tNone;
|
|
}
|
|
|
|
public boolean equals(Object other) {
|
|
if (other instanceof Type) {
|
|
Type type2 = (Type) other;
|
|
return typeSig.equals(type2.typeSig)
|
|
&& instr == type2.instr;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public String toString() {
|
|
if (instr != null)
|
|
return typeSig+"@"+instr.getAddr();
|
|
return typeSig;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 {
|
|
Type[] stack = new Type[bi.getMaxStack()];
|
|
Type[] locals = new Type[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 = (Type[]) stack.clone();
|
|
result.locals = (Type[]) 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(Type type) throws VerifyException {
|
|
reserve(1);
|
|
stack[stackHeight++] = type;
|
|
}
|
|
|
|
public final Type 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++] = Type.tType("N"+ clazzName+";", null);
|
|
else
|
|
info.locals[slot++] = Type.tType("L"+ clazzName+";");
|
|
}
|
|
while (methodType.charAt(pos) != ')') {
|
|
int start = pos;
|
|
pos = TypeSignature.skipType(methodType, pos);
|
|
String paramType = methodType.substring(start, pos);
|
|
info.locals[slot++] = Type.tType(paramType);
|
|
if (TypeSignature.getTypeSize(paramType) == 2)
|
|
info.locals[slot++] = tSecondPart;
|
|
}
|
|
while (slot < bi.getMaxLocals())
|
|
info.locals[slot++] = tNone;
|
|
return info;
|
|
}
|
|
|
|
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++) {
|
|
Type newType = oldInfo.stack[i].mergeType(info.stack[i]);
|
|
if (!newType.equals(oldInfo.stack[i])) {
|
|
if (newType == tNone)
|
|
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++) {
|
|
Type newType = oldInfo.locals[i].mergeType(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", "J", "F", "D", "+", "B", "C", "S"
|
|
};
|
|
String[] arrayTypes = {
|
|
"[I", "[J", "[F", "[D", "[Ljava/lang/Object;", "[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();
|
|
int opcode = instr.getOpcode();
|
|
switch (opcode) {
|
|
case opc_nop:
|
|
case opc_goto:
|
|
break;
|
|
case opc_ldc: {
|
|
Type type;
|
|
Object constant = instr.getConstant();
|
|
if (constant == null)
|
|
type = tNull;
|
|
else if (constant instanceof Integer)
|
|
type = tInt;
|
|
else if (constant instanceof Float)
|
|
type = tFloat;
|
|
else
|
|
type = tString;
|
|
result.push(type);
|
|
break;
|
|
}
|
|
case opc_ldc2_w: {
|
|
Type type;
|
|
Object constant = instr.getConstant();
|
|
if (constant instanceof Long)
|
|
type = tLong;
|
|
else
|
|
type = tDouble;
|
|
result.push(type);
|
|
result.push(tSecondPart);
|
|
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())
|
|
|| ((opcode & 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 ((opcode & 0x1) == 0)
|
|
result.jsrLocals[jsrLength-1].set(instr.getLocalSlot() + 1);
|
|
}
|
|
if ((opcode & 0x1) == 0
|
|
&& result.locals[instr.getLocalSlot()+1] != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
Type type = result.locals[instr.getLocalSlot()];
|
|
if (!type.isOfType(types[opcode - opc_iload]))
|
|
throw new VerifyException(instr.getDescription());
|
|
result.push(type);
|
|
if ((opcode & 0x1) == 0)
|
|
result.push(tSecondPart);
|
|
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 (!result.pop().isOfType("I"))
|
|
throw new VerifyException(instr.getDescription());
|
|
Type arrType = result.pop();
|
|
if (!arrType.isOfType(arrayTypes[opcode - opc_iaload])
|
|
&& (opcode != opc_baload
|
|
|| !arrType.isOfType("[Z")))
|
|
throw new VerifyException(instr.getDescription());
|
|
|
|
String typeSig = arrType.getTypeSig();
|
|
Type elemType = (typeSig.charAt(0) == '['
|
|
? Type.tType(typeSig.substring(1))
|
|
: (opcode == opc_aaload ? tNull
|
|
: Type.tType(types[opcode - opc_iaload])));
|
|
result.push(elemType);
|
|
if (((1 << opcode - opc_iaload) & 0xa) != 0)
|
|
result.push(tSecondPart);
|
|
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())
|
|
|| ((opcode & 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 ((opcode & 0x1) != 0)
|
|
result.jsrLocals[jsrLength-1].set(instr.getLocalSlot() + 1);
|
|
}
|
|
if ((opcode & 0x1) != 0
|
|
&& result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
Type type = result.pop();
|
|
if (!type.isOfType(types[opcode - opc_istore]))
|
|
if (opcode != opc_astore || !type.isOfType("R"))
|
|
throw new VerifyException(instr.getDescription());
|
|
result.locals[instr.getLocalSlot()] = type;
|
|
if ((opcode & 0x1) != 0)
|
|
result.locals[instr.getLocalSlot()+1] = tSecondPart;
|
|
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 << opcode - opc_iastore) & 0xa) != 0
|
|
&& result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
Type type = result.pop();
|
|
if (!result.pop().isOfType("I"))
|
|
throw new VerifyException(instr.getDescription());
|
|
Type arrType = result.pop();
|
|
if (!arrType.isOfType(arrayTypes[opcode - opc_iastore])
|
|
&& (opcode != opc_bastore || !arrType.isOfType("[Z")))
|
|
throw new VerifyException(instr.getDescription());
|
|
String elemType = opcode >= opc_bastore ? "I"
|
|
: types[opcode - opc_iastore];
|
|
if (!type.isOfType(elemType))
|
|
throw new VerifyException(instr.getDescription());
|
|
break;
|
|
}
|
|
case opc_pop: case opc_pop2: {
|
|
int count = opcode - (opc_pop-1);
|
|
result.need(count);
|
|
result.stackHeight -= count;
|
|
break;
|
|
}
|
|
case opc_dup: case opc_dup_x1: case opc_dup_x2: {
|
|
int depth = opcode - opc_dup;
|
|
result.reserve(1);
|
|
result.need(depth+1);
|
|
if (result.stack[result.stackHeight-1] == tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
|
|
int stackdepth = result.stackHeight - (depth + 1);
|
|
if (result.stack[stackdepth] == tSecondPart)
|
|
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 = opcode - opc_dup2;
|
|
result.reserve(2);
|
|
result.need(depth+2);
|
|
if (result.stack[result.stackHeight-2] == tSecondPart)
|
|
throw new VerifyException(instr.getDescription()
|
|
+ " on misaligned long or double");
|
|
int stacktop = result.stackHeight;
|
|
int stackdepth = stacktop - (depth + 2);
|
|
if (result.stack[stackdepth] == tSecondPart)
|
|
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] == tSecondPart
|
|
|| result.stack[result.stackHeight-1] == tSecondPart)
|
|
throw new VerifyException(instr.getDescription()
|
|
+ " on misaligned long or double");
|
|
Type 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[(opcode - opc_iadd) & 3];
|
|
if ((opcode & 1) != 0
|
|
&& result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType(type))
|
|
throw new VerifyException(instr.getDescription());
|
|
if ((opcode & 1) != 0) {
|
|
result.need(2);
|
|
if (result.stack[result.stackHeight-1] != tSecondPart
|
|
|| !result.stack[result.stackHeight-2].isOfType(type))
|
|
throw new VerifyException(instr.getDescription());
|
|
} else {
|
|
result.need(1);
|
|
if (!result.stack[result.stackHeight-1].isOfType(type))
|
|
throw new VerifyException(instr.getDescription());
|
|
}
|
|
break;
|
|
}
|
|
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: {
|
|
String type = types[(opcode - opc_ineg) & 3];
|
|
if ((opcode & 1) != 0) {
|
|
result.need(2);
|
|
if (result.stack[result.stackHeight-1] != tSecondPart
|
|
|| !result.stack[result.stackHeight-2].isOfType(type))
|
|
throw new VerifyException(instr.getDescription());
|
|
} else {
|
|
result.need(1);
|
|
if (!result.stack[result.stackHeight-1].isOfType(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 (!result.pop().isOfType("I"))
|
|
throw new VerifyException(instr.getDescription());
|
|
|
|
if ((opcode & 1) != 0) {
|
|
result.need(2);
|
|
if (result.stack[result.stackHeight-1] != tSecondPart ||
|
|
!result.stack[result.stackHeight-2].isOfType("J"))
|
|
throw new VerifyException(instr.getDescription());
|
|
} else {
|
|
result.need(1);
|
|
if (!result.stack[result.stackHeight-1].isOfType("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 ((opcode & 1) != 0
|
|
&& result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType(types[opcode & 1]))
|
|
throw new VerifyException(instr.getDescription());
|
|
if ((opcode & 1) != 0) {
|
|
result.need(2);
|
|
if (result.stack[result.stackHeight-1] != tSecondPart
|
|
|| !result.stack[result.stackHeight-2].isOfType("J"))
|
|
throw new VerifyException(instr.getDescription());
|
|
} else {
|
|
result.need(1);
|
|
if (!result.stack[result.stackHeight-1].isOfType("I"))
|
|
throw new VerifyException(instr.getDescription());
|
|
}
|
|
break;
|
|
|
|
case opc_iinc:
|
|
if (!result.locals[instr.getLocalSlot()].isOfType("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 = (opcode-opc_i2l)/3;
|
|
int to = (opcode-opc_i2l)%3;
|
|
if (to >= from)
|
|
to++;
|
|
if ((from & 1) != 0
|
|
&& result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType(types[from]))
|
|
throw new VerifyException(instr.getDescription());
|
|
|
|
result.push(Type.tType(types[to]));
|
|
if ((to & 1) != 0)
|
|
result.push(tSecondPart);
|
|
break;
|
|
}
|
|
case opc_i2b: case opc_i2c: case opc_i2s:
|
|
result.need(1);
|
|
if (!result.stack[result.stackHeight-1].isOfType("I"))
|
|
throw new VerifyException(instr.getDescription());
|
|
break;
|
|
|
|
case opc_lcmp:
|
|
if (result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType("J"))
|
|
throw new VerifyException(instr.getDescription());
|
|
if (result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType("J"))
|
|
throw new VerifyException(instr.getDescription());
|
|
result.push(tInt);
|
|
break;
|
|
case opc_dcmpl: case opc_dcmpg:
|
|
if (result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType("D"))
|
|
throw new VerifyException(instr.getDescription());
|
|
if (result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType("D"))
|
|
throw new VerifyException(instr.getDescription());
|
|
result.push(tInt);
|
|
break;
|
|
case opc_fcmpl: case opc_fcmpg:
|
|
if (!result.pop().isOfType("F"))
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType("F"))
|
|
throw new VerifyException(instr.getDescription());
|
|
result.push(tInt);
|
|
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 (!result.pop().isOfType("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 (!result.pop().isOfType("I"))
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType("I"))
|
|
throw new VerifyException(instr.getDescription());
|
|
break;
|
|
case opc_if_acmpeq: case opc_if_acmpne:
|
|
if (!result.pop().isOfType("+"))
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType("+"))
|
|
throw new VerifyException(instr.getDescription());
|
|
break;
|
|
case opc_ifnull: case opc_ifnonnull:
|
|
if (!result.pop().isOfType("+"))
|
|
throw new VerifyException(instr.getDescription());
|
|
break;
|
|
|
|
case opc_ireturn: case opc_lreturn:
|
|
case opc_freturn: case opc_dreturn: case opc_areturn: {
|
|
if (((1 << opcode - opc_ireturn) & 0xa) != 0
|
|
&& result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
Type type = result.pop();
|
|
if (!type.isOfType(types[opcode - opc_ireturn])
|
|
|| !type.isOfType(TypeSignature.getReturnType(methodType)))
|
|
throw new VerifyException(instr.getDescription());
|
|
break;
|
|
}
|
|
case opc_jsr: {
|
|
Instruction jsrTarget = instr.getSingleSucc();
|
|
result.stack[result.stackHeight++] = Type.tType("R", jsrTarget);
|
|
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.tType(type));
|
|
if (TypeSignature.getTypeSize(type) == 2)
|
|
result.push(tSecondPart);
|
|
break;
|
|
}
|
|
case opc_getfield: {
|
|
Reference ref = instr.getReference();
|
|
String classType = ref.getClazz();
|
|
if (!result.pop().isOfType(classType))
|
|
throw new VerifyException(instr.getDescription());
|
|
String type = ref.getType();
|
|
result.push(Type.tType(type));
|
|
if (TypeSignature.getTypeSize(type) == 2)
|
|
result.push(tSecondPart);
|
|
break;
|
|
}
|
|
case opc_putstatic: {
|
|
Reference ref = instr.getReference();
|
|
String type = ref.getType();
|
|
if (TypeSignature.getTypeSize(type) == 2
|
|
&& result.pop() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType(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() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType(type))
|
|
throw new VerifyException(instr.getDescription());
|
|
String classType = ref.getClazz();
|
|
if (!result.pop().isOfType(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() != tSecondPart)
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!result.pop().isOfType(paramTypes[i]))
|
|
throw new VerifyException(instr.getDescription());
|
|
}
|
|
if (ref.getName().equals("<init>")) {
|
|
Type clazz = result.pop();
|
|
String typeSig = clazz.getTypeSig();
|
|
String refClazz = ref.getClazz();
|
|
if (opcode != opc_invokespecial
|
|
|| typeSig.charAt(0) != 'N'
|
|
|| refClazz.charAt(0) != 'L')
|
|
throw new VerifyException(instr.getDescription());
|
|
if (!typeSig.substring(1).equals(refClazz.substring(1))) {
|
|
ClassInfo uci = ClassInfo.forName
|
|
(typeSig.substring(1, typeSig.length()-1)
|
|
.replace('/', '.'));
|
|
if (uci.getSuperclass()
|
|
!= TypeSignature.getClassInfo(refClazz)
|
|
|| clazz.getInstruction() != null)
|
|
throw new VerifyException(instr.getDescription());
|
|
}
|
|
Type newType = Type.tType("L" + typeSig.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 (opcode != opc_invokestatic) {
|
|
String classType = ref.getClazz();
|
|
if (!result.pop().isOfType(classType))
|
|
throw new VerifyException(instr.getDescription());
|
|
}
|
|
String type = TypeSignature.getReturnType(refmt);
|
|
if (!type.equals("V")) {
|
|
result.push(Type.tType(type));
|
|
if (TypeSignature.getTypeSize(type) == 2)
|
|
result.push(tSecondPart);
|
|
}
|
|
break;
|
|
}
|
|
case opc_new: {
|
|
String clName = instr.getClazzType();
|
|
result.stack[result.stackHeight++] =
|
|
Type.tType("N" + clName.substring(1), instr);
|
|
break;
|
|
}
|
|
case opc_arraylength: {
|
|
if (!result.pop().isOfType("[*"))
|
|
throw new VerifyException(instr.getDescription());
|
|
result.push(tInt);
|
|
break;
|
|
}
|
|
case opc_athrow: {
|
|
if (!result.pop().isOfType("Ljava/lang/Throwable;"))
|
|
throw new VerifyException(instr.getDescription());
|
|
break;
|
|
}
|
|
case opc_checkcast: {
|
|
String classType = instr.getClazzType();
|
|
if (!result.pop().isOfType("+"))
|
|
throw new VerifyException(instr.getDescription());
|
|
result.push(Type.tType(classType));
|
|
break;
|
|
}
|
|
case opc_instanceof: {
|
|
if (!result.pop().isOfType("Ljava/lang/Object;"))
|
|
throw new VerifyException(instr.getDescription());
|
|
result.push(tInt);
|
|
break;
|
|
}
|
|
case opc_monitorenter:
|
|
case opc_monitorexit:
|
|
if (!result.pop().isOfType("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 (!result.pop().isOfType("I"))
|
|
throw new VerifyException(instr.getDescription());
|
|
String classType = instr.getClazzType();
|
|
result.push(Type.tType(classType));
|
|
break;
|
|
}
|
|
default:
|
|
throw new AssertError("Invalid opcode "+opcode);
|
|
}
|
|
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();
|
|
int opcode = instr.getOpcode();
|
|
if (opcode == opc_ret) {
|
|
Type retVarType = prevInfo.locals[instr.getLocalSlot()];
|
|
if (prevInfo.jsrTargets == null
|
|
|| !retVarType.isOfType("R"))
|
|
throw new VerifyException(instr.getDescription());
|
|
int jsrLength = prevInfo.jsrTargets.length - 1;
|
|
Instruction jsrTarget = retVarType.getInstruction();
|
|
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 (opcode == 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 (prevInfo.locals[j]
|
|
.getTypeSig().charAt(0) == 'N')
|
|
throw new VerifyException
|
|
("Uninitialized local in back-branch");
|
|
}
|
|
for (int j = 0; j < prevInfo.stackHeight; j++) {
|
|
if (prevInfo.stack[j]
|
|
.getTypeSig().charAt(0) == 'N')
|
|
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 (prevInfo.locals[j]
|
|
.getTypeSig().charAt(0) == 'N')
|
|
throw new VerifyException
|
|
("Uninitialized local in try block");
|
|
}
|
|
VerifyInfo excInfo = (VerifyInfo) prevInfo.clone();
|
|
excInfo.stackHeight = 1;
|
|
if (handlers[i].type != null)
|
|
excInfo.stack[0] =
|
|
Type.tType("L" + handlers[i].type
|
|
.replace('.', '/') + ";");
|
|
else
|
|
excInfo.stack[0]
|
|
= Type.tType("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;
|
|
}
|
|
}
|
|
}
|
|
|