From b53430f5c599d598daf1eeeb2e872c880aa3e57b Mon Sep 17 00:00:00 2001 From: hoenicke Date: Mon, 3 Jul 2000 12:52:25 +0000 Subject: [PATCH] Adapted to new bytecode package. git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1245 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/jvm/CodeVerifier.java | 1523 +++++++++++++++++ jode/jode/jvm/CodeVerifier.java.in | 1127 ------------ .../{Interpreter.java.in => Interpreter.java} | 76 +- jode/jode/jvm/Makefile.am | 8 +- ...nalyzer.java.in => SyntheticAnalyzer.java} | 194 ++- 5 files changed, 1701 insertions(+), 1227 deletions(-) create mode 100644 jode/jode/jvm/CodeVerifier.java delete mode 100644 jode/jode/jvm/CodeVerifier.java.in rename jode/jode/jvm/{Interpreter.java.in => Interpreter.java} (93%) rename jode/jode/jvm/{SyntheticAnalyzer.java.in => SyntheticAnalyzer.java} (69%) diff --git a/jode/jode/jvm/CodeVerifier.java b/jode/jode/jvm/CodeVerifier.java new file mode 100644 index 0000000..c008128 --- /dev/null +++ b/jode/jode/jvm/CodeVerifier.java @@ -0,0 +1,1523 @@ +/* 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.BasicBlocks; +import jode.bytecode.Block; +import jode.bytecode.ClassPath; +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.io.IOException; +import java.util.BitSet; +///#def COLLECTIONS java.util +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +///#enddef + +/** + * Verifies a given method. + * + * @author Jochen Hoenicke + */ +public class CodeVerifier implements Opcodes { + ClassInfo ci; + MethodInfo mi; + BasicBlocks bb; + ClassPath classpath; + + String methodType; + + Type returnType; + Type tInt; + Type tLong; + Type tFloat; + Type tDouble; + Type tNone; + Type tSecondPart; + + Type tString; + Type tObject; + + Map typeHash = new HashMap(); + + private Type tType(String typeSig) { + Type type = (Type) typeHash.get(typeSig); + if (type == null) { + int obj = typeSig.charAt(0) == 'N' ? 0 : typeSig.indexOf('L'); + int semi = typeSig.indexOf(';'); + if (obj != -1 && semi != -1) { + String subTypeSig = typeSig.substring(0, obj+1) + + typeSig.substring(semi+1); + String className + = typeSig.substring(obj+1, semi).replace('/', '.'); + ClassInfo classInfo = classpath.getClassInfo(className); + type = new Type(subTypeSig, classInfo, null); + } else + type = new Type(typeSig, null, null); + typeHash.put(typeSig, type); + } + return type; + } + + private Type tType(Block jsrTarget) { + Type type = (Type) typeHash.get(jsrTarget); + if (type == null) { + type = new Type("R", null, jsrTarget); + typeHash.put(jsrTarget, type); + } + return type; + } + + private Type tType(String head, ClassInfo classInfo) { + String typeSig = head + classInfo.getName().replace('.', '/') + ';'; + Type type = (Type) typeHash.get(typeSig); + if (type == null) { + type = new Type(head, classInfo, null); + typeHash.put(typeSig, type); + } + return type; + } + + /** + * We need some more types, than mentioned in jvm. + */ + private static class Type { + /* "ZBCSIFJD" are the normal primitive types. + * "V" stands for void type. + * "L" is normal class type, the class is in classInfo field. + * "[..." is normal array type + * "?" stands for type error + * "N" stands for the uninitialized this of a constructor, + * and classInfo is set. + * "Nxxx" stands for a new uninitialized type, where xxx is + * a unique identifier for the new instruction + * and classInfo is set. + * "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 classInfo if this is or contains a class type. + */ + private ClassInfo classInfo; + + /** + * The target block of the jsr if this is a "R" type. + */ + private Block jsrTarget; + + public Type(String typeSig, ClassInfo classInfo, Block jsrTarget) { + if ((typeSig.indexOf('L') >= 0 || typeSig.charAt(0) == 'N') + && classInfo == null) + throw new IllegalArgumentException(); + this.typeSig = typeSig; + this.classInfo = classInfo; + this.jsrTarget = jsrTarget; + } + + public String getTypeSig() { + return typeSig; + } + + public Block getJsrTarget() { + return jsrTarget; + } + + /** + * @param t2 the type signature of the type to check for. + * This may be one of the special signatures: + *
"[*"
array of something
+ *
"+"
(uninitialized) object/returnvalue type
+ *
"L"
(initialized) object/returnvalue type
+ *
"2", "R"
as the typeSig parameter
+ *
+ * @return true, iff this is castable to t2 by a + * widening cast. */ + public boolean isOfType(Type destType) { + String thisSig = typeSig; + String destSig = destType.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; + case 'L': + if (c1 == '0') + return true; + if ("L[".indexOf(c1) < 0) + return false; + + ClassInfo wantedType = destType.classInfo; + if (wantedType == null + || wantedType.getName() == "java.lang.Object") + return true; + + try { + wantedType.load(ClassInfo.HIERARCHY); + if (wantedType.isInterface()) + return true; + if (c1 == 'L') + return wantedType.superClassOf(classInfo); + } catch (IOException ex) { + GlobalOptions.err.println + ("WARNING: Can't get full hierarchy of " + + wantedType + "."); + return true; + } + case 'N': + if (typeSig.charAt(0) != 'N') + return false; + + /* New types must match exactly ... */ + if (this.classInfo == destType.classInfo) + return true; + + /* ... except that a constructor can call the super + * constructor */ + if (typeSig.length() > 1) + return false; + + try { + classInfo.load(ClassInfo.HIERARCHY); + return (classInfo.getSuperclass() == destType.classInfo); + } catch (IOException ex) { + /* ignore, type is maybe correct. */ + return true; + } + } + return false; + } + + /** + * @return The common super type of this and type2. + */ + public Type mergeType(CodeVerifier cv, 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 : cv.tNone; + if (c2 == '0') + return ("L[".indexOf(c1) >= 0) ? this : cv.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++; + } + + if (c1 == '[' || c2 == '[') { + // Only one of them is array now, the other must be an + // object, the common super is tObject + if (c1 == 'L' || c2 == 'L') { + if (dimensions == 0) + return cv.tObject; + StringBuffer result = new StringBuffer(dimensions + 18); + for (int i=0; i< dimensions; i++) + result.append("["); + result.append("Ljava/lang/Object;"); + return cv.tType(result.toString()); + } + return cv.tNone; + } + + if (c1 == 'L' && c2 == 'L') { + ClassInfo clazz1 = classInfo; + ClassInfo clazz2 = type2.classInfo; + try { + if (clazz1.superClassOf(clazz2)) + return this; + } catch (IOException ex) { + /* clazz1 has no complete hierarchy, we can assume + * that it extends class2. + */ + return this; + } + try { + if (clazz2.superClassOf(clazz1)) + return type2; + } catch (IOException ex) { + /* clazz1 has no complete hierarchy, we can assume + * that it extends class2. + */ + return this; + } + /* Now the complete hierarchy of clazz1 and + * clazz2 is loaded */ + try { + do { + clazz1 = clazz1.getSuperclass(); + } while (!clazz1.superClassOf(clazz2)); + } catch (IOException ex) { + throw new InternalError("Hierarchy vanished?"); + } + StringBuffer result = new StringBuffer + (dimensions + clazz1.getName().length() + 2); + for (int i=0; i< dimensions; i++) + result.append("["); + result.append("L"); + return cv.tType(result.toString(), clazz1); + } + return cv.tNone; + } + + public boolean equals(Object other) { + if (other instanceof Type) { + Type type2 = (Type) other; + return typeSig.equals(type2.typeSig) + && classInfo == type2.classInfo + && jsrTarget == type2.jsrTarget; + } + return false; + } + + public String toString() { + StringBuffer sb = new StringBuffer(typeSig); + if (classInfo != null) + sb.append(classInfo.getName()); + if (jsrTarget != null) + sb.append(jsrTarget); + return sb.toString(); + } + } + + /** + * 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. + */ + final class JsrUsedInfo { + /** + * The last jsrTarget that must have been traversed. + */ + Block jsrTarget; + + /** + * The set of locals changed since the last jsrTarget. + */ + BitSet jsrUsed; + + JsrUsedInfo(Block jsrTarget, BitSet jsrUsed) { + this.jsrTarget = jsrTarget; + this.jsrUsed = jsrUsed; + } + + JsrUsedInfo(JsrUsedInfo orig) { + this.jsrTarget = orig.jsrTarget; + this.jsrUsed = orig.jsrUsed; + } + + public String toString() { + return ""+jsrTarget+'-'+jsrUsed; + } + } + + class SubroutineInfo { + /** + * The previous used Info, null if this is the outermost jsr. + */ + JsrUsedInfo prevInfo; + /** + * Block number of the return. + */ + Block retBlock = null; + /** + * The VerifyInfo after the ret. + */ + VerifyInfo retInfo; + /** + * The locals used in this subroutine + */ + BitSet usedLocals; + /** + * The bitset containing the numbers of the blocks following + * that may follow a JSR to this subroutine. + */ + BitSet jsrSuccessors = new BitSet(); + } + + /** + * The VerifyInfo contains informations about the state of the stack + * and local variables, as well as the current subroutine. + * + * We create a VerifyInfo for every reachable basic block in the + * verifyInfos array. For not yet reached basic blocks, that are + * the successor of an jsr, it contains the state just _before_ + * the jsr. If later a ret is found, it will take care to correct + * that. + * + * We also have an intermediate VerifyInfo, which is modified + * while we are "simulating" the instructions in a basic block. + * After the basic block is fully simulated, we merge that temporary + * VerifyInfo (cloning() it if necessary) and free it afterwards. + * + * Last but not least, we have a VerifyInfo in SubroutineInfo, that + * records the local/stack state just after the ret. + * + * For information about typechecking jsrs: + * + * 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. + * + * The difficult part are subroutines inside a method (the jsr and + * ret instructions). We remember the last jsrtarget that must be + * traversed to reach this block (a jsrTarget is a basic block to + * which a jsr jumps). Since a jsrTarget has a "R" + * type on the stack there is no possibility to reach a jsrTarget + * on another way. + * + * We only remember the innermost subroutine, but its SubroutineInfo + * will contain the information about outer subroutines. + * + * If we change a local we remember it in jsrUsed. This is + * needed for the local merging on a ret as specified in the jvm + * spec. + */ + final class VerifyInfo implements Cloneable { + /** + * The jsr and used info for the innermost surrounding jsr. + * Normally this is null. + */ + JsrUsedInfo jsrInfo; + + /** + * The current stack height + */ + int stackHeight = 0; + /** + * The types currently on the stack. The entries at indices + * bigger or equal stackHeight are _undefined_. + * @see Type + */ + Type[] stack = new Type[bb.getMaxStack()]; + + /** + * The types currently in local slots. An entry is null, if + * the local may not be used. + * @see Type + */ + Type[] locals = new Type[bb.getMaxLocals()]; + + public Object clone() { + try { + VerifyInfo result = (VerifyInfo) super.clone(); + result.stack = (Type[]) stack.clone(); + result.locals = (Type[]) locals.clone(); + if (jsrInfo != null) + result.jsrInfo = new JsrUsedInfo(jsrInfo); + return result; + } catch(CloneNotSupportedException ex) { + throw new AssertError("Clone not supported?"); + } + } + + public final void reserve(int count) throws VerifyException { + if (stackHeight + count > 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= bb.getMaxLocals()) + throw new VerifyException("Too few local slots"); + if (mi.getName().equals("")) + info.locals[slot++] = tType("N", ci); + else + info.locals[slot++] = tType("L", ci); + } + while (methodType.charAt(pos) != ')') { + int start = pos; + pos = TypeSignature.skipType(methodType, pos); + String paramType = methodType.substring(start, pos); + if (slot >= bb.getMaxLocals()) + throw new VerifyException("Too few local slots"); + info.locals[slot++] = tType(paramType); + if (TypeSignature.getTypeSize(paramType) == 2) { + if (slot >= bb.getMaxLocals()) + throw new VerifyException("Too few local slots"); + info.locals[slot++] = tSecondPart; + } + } + while (slot < bb.getMaxLocals()) + info.locals[slot++] = tNone; + return info; + } + + /** + * Merges the second JsrUsedInfo into the first. + * @return true if first JsrUsedInfo changed in this merge, i.e. + * got more specific. + */ + private boolean mergeJsrTarget(JsrUsedInfo first, JsrUsedInfo second) { + /* trivial cases first. */ + if (first.jsrTarget == second.jsrTarget) + return false; + if (first.jsrTarget == null || second.jsrTarget == null) + return false; + + /* Now the bitsets can't be null */ + int firstDepth = 0; + for (JsrUsedInfo t = first; t != null; + t = subInfos[t.jsrTarget.getBlockNr()].prevInfo) + firstDepth++; + int secondDepth = 0; + for (JsrUsedInfo t = second; t != null; + t = subInfos[t.jsrTarget.getBlockNr()].prevInfo) + secondDepth++; + + boolean changed = false; + Block secondTarget = second.jsrTarget; + while (firstDepth > secondDepth) { + JsrUsedInfo firstPrev + = subInfos[first.jsrTarget.getBlockNr()].prevInfo; + if (firstPrev == null) + first.jsrTarget = null; + else + first.jsrTarget = firstPrev.jsrTarget; + firstDepth--; + } + while (secondDepth > firstDepth) { + changed = true; + JsrUsedInfo secondPrev + = subInfos[secondTarget.getBlockNr()].prevInfo; + if (secondPrev != null) { + first.jsrUsed.or(secondPrev.jsrUsed); + secondTarget = secondPrev.jsrTarget; + } else + secondTarget = null; + secondDepth--; + } + while (first.jsrTarget != secondTarget) { + changed = true; + JsrUsedInfo firstPrev + = subInfos[first.jsrTarget.getBlockNr()].prevInfo; + if (firstPrev == null) + first.jsrTarget = null; + else + first.jsrTarget = firstPrev.jsrTarget; + JsrUsedInfo secondPrev + = subInfos[secondTarget.getBlockNr()].prevInfo; + if (secondPrev != null) { + first.jsrUsed.or(secondPrev.jsrUsed); + secondTarget = secondPrev.jsrTarget; + } else + secondTarget = null; + } + return changed; + } + + private boolean mergeInfo(Block block, VerifyInfo info) + throws VerifyException { + int blockNr = block.getBlockNr(); + if (verifyInfos[blockNr] == null) { + verifyInfos[blockNr] = (VerifyInfo) info.clone(); + return true; + } + boolean changed = false; + VerifyInfo oldInfo = verifyInfos[blockNr]; + if (oldInfo.stackHeight != info.stackHeight) + throw new VerifyException("Stack height differ at: " + blockNr); + for (int i=0; i < oldInfo.stackHeight; i++) { + Type newType = oldInfo.stack[i].mergeType(this, 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 < bb.getMaxLocals(); i++) { + Type newType = oldInfo.locals[i].mergeType(this, info.locals[i]); + if (!newType.equals(oldInfo.locals[i])) { + changed = true; + oldInfo.locals[i] = newType; + } + } + if (oldInfo.jsrInfo != null) { + if (info.jsrInfo == null) { + oldInfo.jsrInfo = null; + changed = true; + } else if (mergeJsrTarget(oldInfo.jsrInfo, info.jsrInfo)) { + if (oldInfo.jsrInfo.jsrTarget == null) + oldInfo.jsrInfo = null; + changed = true; + } + } + return changed; + } + + private void modelEffect(Instruction instr, VerifyInfo info) + throws VerifyException { + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_VERIFIER) != 0) + GlobalOptions.err.println(""+info+instr); + 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 = tType("0"); + else if (constant instanceof Integer) + type = tInt; + else if (constant instanceof Float) + type = tFloat; + else + type = tString; + info.push(type); + break; + } + case opc_ldc2_w: { + Type type; + Object constant = instr.getConstant(); + if (constant instanceof Long) + type = tLong; + else + type = tDouble; + info.push(type); + info.push(tSecondPart); + break; + } + case opc_iload: + case opc_lload: + case opc_fload: + case opc_dload: + case opc_aload: { + if (info.jsrInfo != null) { + info.jsrInfo.jsrUsed.set(instr.getLocalSlot()); + if ((opcode & 0x1) == 0) + info.jsrInfo.jsrUsed.set(instr.getLocalSlot() + 1); + } + if ((opcode & 0x1) == 0 + && info.locals[instr.getLocalSlot()+1] != tSecondPart) + throw new VerifyException(instr.getDescription()); + Type type = info.locals[instr.getLocalSlot()]; + if (!type.isOfType(types[opcode - opc_iload])) + throw new VerifyException(instr.getDescription()); + info.push(type); + if ((opcode & 0x1) == 0) + info.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 (!info.pop().isOfType(tInt)) + throw new VerifyException(instr.getDescription()); + Type arrType = info.pop(); + if (!arrType.isOfType(arrayTypes[opcode - opc_iaload]) + && (opcode != opc_baload + || !arrType.isOfType(tType("[Z")))) + throw new VerifyException(instr.getDescription()); + + String typeSig = arrType.getTypeSig(); + Type elemType; + if (typeSig.charAt(0) == '[') { + if (arrType.classInfo != null) + elemType = tType(typeSig.substring(1), arrType.classInfo); + else + elemType = tType(typeSig.substring(1)); + } else if(opcode == opc_aaload) + elemType = tType("0"); + else + elemType = types[opcode - opc_iaload]; + info.push(elemType); + if (((1 << opcode - opc_iaload) & 0xa) != 0) + info.push(tSecondPart); + break; + } + case opc_istore: case opc_lstore: + case opc_fstore: case opc_dstore: case opc_astore: { + if (info.jsrInfo != null) { + info.jsrInfo.jsrUsed.set(instr.getLocalSlot()); + if ((opcode & 0x1) != 0) + info.jsrInfo.jsrUsed.set(instr.getLocalSlot()+1); + } + if ((opcode & 0x1) != 0 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + Type type = info.pop(); + if (!type.isOfType(types[opcode - opc_istore])) + if (opcode != opc_astore || !type.isOfType(tType("R"))) + throw new VerifyException(instr.getDescription()); + info.locals[instr.getLocalSlot()] = type; + if ((opcode & 0x1) != 0) + info.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 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + Type type = info.pop(); + if (!info.pop().isOfType(tInt)) + throw new VerifyException(instr.getDescription()); + Type arrType = info.pop(); + if (!arrType.isOfType(arrayTypes[opcode - opc_iastore]) + && (opcode != opc_bastore || !arrType.isOfType(tType("[Z")))) + throw new VerifyException(instr.getDescription()); + Type elemType = opcode >= opc_bastore ? tInt + : 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); + info.need(count); + info.stackHeight -= count; + break; + } + case opc_dup: case opc_dup_x1: case opc_dup_x2: { + int depth = opcode - opc_dup; + info.reserve(1); + info.need(depth+1); + if (info.stack[info.stackHeight-1] == tSecondPart) + throw new VerifyException(instr.getDescription()); + + int stackdepth = info.stackHeight - (depth + 1); + if (info.stack[stackdepth] == tSecondPart) + throw new VerifyException(instr.getDescription() + + " on long or double"); + for (int i=info.stackHeight; i > stackdepth; i--) + info.stack[i] = info.stack[i-1]; + info.stack[stackdepth] = info.stack[info.stackHeight++]; + break; + } + case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: { + int depth = opcode - opc_dup2; + info.reserve(2); + info.need(depth+2); + if (info.stack[info.stackHeight-2] == tSecondPart) + throw new VerifyException(instr.getDescription() + + " on misaligned long or double"); + int stacktop = info.stackHeight; + int stackdepth = stacktop - (depth + 2); + if (info.stack[stackdepth] == tSecondPart) + throw new VerifyException(instr.getDescription() + + " on long or double"); + for (int i=stacktop; i > stackdepth; i--) + info.stack[i+1] = info.stack[i-1]; + info.stack[stackdepth+1] = info.stack[stacktop+1]; + info.stack[stackdepth] = info.stack[stacktop]; + info.stackHeight+=2; + break; + } + case opc_swap: { + info.need(2); + if (info.stack[info.stackHeight-2] == tSecondPart + || info.stack[info.stackHeight-1] == tSecondPart) + throw new VerifyException(instr.getDescription() + + " on misaligned long or double"); + Type tmp = info.stack[info.stackHeight-1]; + info.stack[info.stackHeight-1] = + info.stack[info.stackHeight-2]; + info.stack[info.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: { + Type type = types[(opcode - opc_iadd) & 3]; + if ((opcode & 1) != 0 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(type)) + throw new VerifyException(instr.getDescription()); + if ((opcode & 1) != 0) { + info.need(2); + if (info.stack[info.stackHeight-1] != tSecondPart + || !info.stack[info.stackHeight-2].isOfType(type)) + throw new VerifyException(instr.getDescription()); + } else { + info.need(1); + if (!info.stack[info.stackHeight-1].isOfType(type)) + throw new VerifyException(instr.getDescription()); + } + break; + } + case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: { + Type type = types[(opcode - opc_ineg) & 3]; + if ((opcode & 1) != 0) { + info.need(2); + if (info.stack[info.stackHeight-1] != tSecondPart + || !info.stack[info.stackHeight-2].isOfType(type)) + throw new VerifyException(instr.getDescription()); + } else { + info.need(1); + if (!info.stack[info.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 (!info.pop().isOfType(tInt)) + throw new VerifyException(instr.getDescription()); + + if ((opcode & 1) != 0) { + info.need(2); + if (info.stack[info.stackHeight-1] != tSecondPart || + !info.stack[info.stackHeight-2].isOfType(tLong)) + throw new VerifyException(instr.getDescription()); + } else { + info.need(1); + if (!info.stack[info.stackHeight-1].isOfType(tInt)) + 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 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(types[opcode & 1])) + throw new VerifyException(instr.getDescription()); + if ((opcode & 1) != 0) { + info.need(2); + if (info.stack[info.stackHeight-1] != tSecondPart + || !info.stack[info.stackHeight-2].isOfType(tLong)) + throw new VerifyException(instr.getDescription()); + } else { + info.need(1); + if (!info.stack[info.stackHeight-1].isOfType(tInt)) + throw new VerifyException(instr.getDescription()); + } + break; + + case opc_iinc: + if (!info.locals[instr.getLocalSlot()].isOfType(tInt)) + 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 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(types[from])) + throw new VerifyException(instr.getDescription()); + + info.push(types[to]); + if ((to & 1) != 0) + info.push(tSecondPart); + break; + } + case opc_i2b: case opc_i2c: case opc_i2s: + info.need(1); + if (!info.stack[info.stackHeight-1].isOfType(tInt)) + throw new VerifyException(instr.getDescription()); + break; + + case opc_lcmp: + if (info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tLong)) + throw new VerifyException(instr.getDescription()); + if (info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tLong)) + throw new VerifyException(instr.getDescription()); + info.push(tInt); + break; + case opc_dcmpl: case opc_dcmpg: + if (info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tDouble)) + throw new VerifyException(instr.getDescription()); + if (info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tDouble)) + throw new VerifyException(instr.getDescription()); + info.push(tInt); + break; + case opc_fcmpl: case opc_fcmpg: + if (!info.pop().isOfType(tFloat)) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tFloat)) + throw new VerifyException(instr.getDescription()); + info.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 (!info.pop().isOfType(tInt)) + 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 (!info.pop().isOfType(tInt)) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tInt)) + throw new VerifyException(instr.getDescription()); + break; + case opc_if_acmpeq: case opc_if_acmpne: + if (!info.pop().isOfType(tType("+"))) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tType("+"))) + throw new VerifyException(instr.getDescription()); + break; + case opc_ifnull: case opc_ifnonnull: + if (!info.pop().isOfType(tType("+"))) + 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 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + Type type = info.pop(); + if (!type.isOfType(types[opcode - opc_ireturn]) + || !type.isOfType(returnType)) + throw new VerifyException(instr.getDescription()); + break; + } + case opc_jsr: + case opc_ret: + // handled in main loop + break; + case opc_return: + if (!returnType.typeSig.equals("V")) + throw new VerifyException(instr.getDescription()); + break; + case opc_getstatic: { + Reference ref = instr.getReference(); + String type = ref.getType(); + info.push(tType(type)); + if (TypeSignature.getTypeSize(type) == 2) + info.push(tSecondPart); + break; + } + case opc_getfield: { + Reference ref = instr.getReference(); + Type classType = tType(ref.getClazz()); + if (!info.pop().isOfType(classType)) + throw new VerifyException(instr.getDescription()); + String type = ref.getType(); + info.push(tType(type)); + if (TypeSignature.getTypeSize(type) == 2) + info.push(tSecondPart); + break; + } + case opc_putstatic: { + Reference ref = instr.getReference(); + String type = ref.getType(); + if (TypeSignature.getTypeSize(type) == 2 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tType(type))) + throw new VerifyException(instr.getDescription()); + break; + } + case opc_putfield: { + Reference ref = instr.getReference(); + String type = ref.getType(); + if (TypeSignature.getTypeSize(type) == 2 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tType(type))) + throw new VerifyException(instr.getDescription()); + Type classType = tType(ref.getClazz()); + if (!info.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 + && info.pop() != tSecondPart) + throw new VerifyException(instr.getDescription()); + if (!info.pop().isOfType(tType(paramTypes[i]))) + throw new VerifyException(instr.getDescription()); + } + if (ref.getName().equals("")) { + Type clazz = info.pop(); + String typeSig = clazz.getTypeSig(); + String refClazzSig = ref.getClazz(); + Type refClazz = tType("N" + refClazzSig.substring(1)); + if (opcode != opc_invokespecial + || refClazzSig.charAt(0) != 'L' + || !clazz.isOfType(refClazz)) + throw new VerifyException(instr.getDescription()); + Type newType = tType("L" + clazz.classInfo.getName() + .replace('.','/')+";"); + for (int i=0; i< info.stackHeight; i++) + if (info.stack[i] == clazz) + info.stack[i] = newType; + for (int i=0; i< info.locals.length; i++) + if (info.locals[i] == clazz) + info.locals[i] = newType; + } else if (opcode != opc_invokestatic) { + Type classType = tType(ref.getClazz()); + if (!info.pop().isOfType(classType)) + throw new VerifyException(instr.getDescription()); + } + String type = TypeSignature.getReturnType(refmt); + if (!type.equals("V")) { + info.push(tType(type)); + if (TypeSignature.getTypeSize(type) == 2) + info.push(tSecondPart); + } + break; + } + case opc_new: { + String clName = instr.getClazzType(); + info.stack[info.stackHeight++] = + tType("N" + clName.substring(1) + instr.hashCode()); + break; + } + case opc_arraylength: { + if (!info.pop().isOfType(tType("[*"))) + throw new VerifyException(instr.getDescription()); + info.push(tInt); + break; + } + case opc_athrow: { + if (!info.pop().isOfType(tType("Ljava/lang/Throwable;"))) + throw new VerifyException(instr.getDescription()); + break; + } + case opc_checkcast: { + Type classType = tType(instr.getClazzType()); + if (!info.pop().isOfType(tType("+"))) + throw new VerifyException(instr.getDescription()); + info.push(classType); + break; + } + case opc_instanceof: { + if (!info.pop().isOfType(tType("Ljava/lang/Object;"))) + throw new VerifyException(instr.getDescription()); + info.push(tInt); + break; + } + case opc_monitorenter: + case opc_monitorexit: + if (!info.pop().isOfType(tType("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 (!info.pop().isOfType(tInt)) + throw new VerifyException(instr.getDescription()); + Type classType = tType(instr.getClazzType()); + info.push(classType); + break; + } + default: + throw new AssertError("Invalid opcode "+opcode); + } + } + + /* We manually program a bitset, since the best features are + * missing in jdk 1.1. + */ + private class MyBitSet { + int[] data; + // This is always smaller than the first set bit. + int firstBit; + + public MyBitSet(int maxLength) { + data = new int[(maxLength + 31) / 32]; + firstBit = 0; + } + + public void set(int bit) { + data[bit >> 5] |= 1 << (bit & 0x1f); + if (bit < firstBit) + firstBit = bit; + } + + public void clear(int bit) { + data[bit >> 5] &= ~(1 << (bit & 0x1f)); + } + + public int findFirst() { + int first = firstBit >> 5; + while (data[first] == 0) { + first++; + firstBit = first << 5; + } + int bitmask = data[first] >> (firstBit & 0x1f); + while ((bitmask & 1) == 0) { + bitmask >>= 1; + firstBit++; + } + return firstBit; + } + + public boolean isEmpty() { + for (int i = firstBit >> 5; i < data.length; i++) { + if (data[i] != 0) + return false; + } + return true; + } + } + + private void doVerify() throws VerifyException { + Block[] blocks = bb.getBlocks(); + int len = blocks.length; + verifyInfos = new VerifyInfo[len]; + beforeJsrs = new VerifyInfo[len]; + subInfos = new SubroutineInfo[len]; + + MyBitSet todoSet = new MyBitSet(blocks.length); + Block firstBlock = bb.getStartBlock(); + if (firstBlock == null) { + /* empty method is okay */ + return; + } + verifyInfos[firstBlock.getBlockNr()] = initInfo(); + todoSet.set(firstBlock.getBlockNr()); + while (!todoSet.isEmpty()) { + int blockNr = todoSet.findFirst(); + todoSet.clear(blockNr); + Block block = blocks[blockNr]; + VerifyInfo info = (VerifyInfo) verifyInfos[blockNr].clone(); + + Handler[] catchers = block.getCatchers(); + if (catchers.length > 0) { + VerifyInfo excInfo = (VerifyInfo) info.clone(); + + for (int j = 0; j < info.locals.length; j++) { + if (info.locals[j].getTypeSig().charAt(0) == 'N') + throw new VerifyException + ("Uninitialized local in try block"); + } + excInfo.stackHeight = 1; + for (int i=0; i < catchers.length; i++) { + String type = catchers[i].getType(); + if (type != null) + excInfo.stack[0] = + tType("L" + type.replace('.', '/') + ";"); + else + excInfo.stack[0] + = tType("Ljava/lang/Throwable;"); + Block catcher = catchers[i].getCatcher(); + if (mergeInfo(catcher, excInfo)) + todoSet.set(catcher.getBlockNr()); + } + } + + + Instruction instr = null; + Iterator iter = block.getInstructions().iterator(); + while (iter.hasNext()) { + instr = (Instruction) iter.next(); + modelEffect(instr, info); + + if (catchers.length > 0 && instr.isStore()) { + for (int i=0; i < catchers.length; i++) { + int slot = instr.getLocalSlot(); + if (info.locals[slot].getTypeSig().charAt(0) == 'N') + throw new VerifyException + ("Uninitialized local in try block"); + + Block catcher = catchers[i].getCatcher(); + int catcherNr = catcher.getBlockNr(); + VerifyInfo oldInfo = verifyInfos[catcherNr]; + Type newType = oldInfo.locals[slot] + .mergeType(this, info.locals[slot]); + if (!newType.equals(oldInfo.locals[slot])) { + oldInfo.locals[slot] = newType; + todoSet.set(catcherNr); + } + } + } + } + + int opcode = instr.getOpcode(); + if (opcode == opc_jsr) { + Block jsrTarget = block.getSuccs()[0]; + Block nextBlock = block.getSuccs()[1]; + + if (info.jsrInfo != null) { + // Check for recursive jsrs. + for (JsrUsedInfo jui = info.jsrInfo; + jui != null; + jui = subInfos[jui.jsrTarget.getBlockNr()] + .prevInfo) { + // Don't assume this is recursive, but assume + // that the previous rets were left instead. + //XXXXXXXXXXXXXXXXX + if (jui.jsrTarget == jsrTarget) { + // This is a recursive jsr. Or the previous + // invocation of the jsr terminated without a + // ret. We forbid this! XXX I think this too + // harsh, but doing it right is very difficult, + // so I stay on secure side. + throw new VerifyException("Recursive JSR!"); + } + } + } + + // Create the VerifyInfo for the state after the jsr + // is performed. + VerifyInfo targetInfo = (VerifyInfo) info.clone(); + targetInfo.push(tType(jsrTarget)); + targetInfo.jsrInfo + = new JsrUsedInfo(jsrTarget, new BitSet()); + // Merge the target info + if (mergeInfo(jsrTarget, targetInfo)) + todoSet.set(jsrTarget.getBlockNr()); + + SubroutineInfo subInfo = subInfos[jsrTarget.getBlockNr()]; + // Create the subroutine info if it doesn't yet exists. + if (subInfo == null) { + subInfo = new SubroutineInfo(); + subInfos[jsrTarget.getBlockNr()] = subInfo; + if (info.jsrInfo != null) + subInfo.prevInfo = new JsrUsedInfo(info.jsrInfo); + } else { + boolean changed; + if (info.jsrInfo != null) { + changed = mergeJsrTarget + (subInfo.prevInfo, info.jsrInfo); + if (subInfo.prevInfo.jsrTarget == null) + subInfo.prevInfo = null; + } else { + subInfo.prevInfo = null; + changed = true; + } + if (changed + && subInfos[jsrTarget.getBlockNr()].retBlock != null) + todoSet.set(subInfos[jsrTarget.getBlockNr()] + .retBlock.getBlockNr()); + } + + if (nextBlock != null) { + // Add our successor to the successor list. + subInfo.jsrSuccessors.set(nextBlock.getBlockNr()); + + if (subInfo.retInfo != null) { + // The jsr target already knows its return + // instruction, we do the ret merging immediately + VerifyInfo retInfo = subInfo.retInfo; + info.stack = retInfo.stack; + info.stackHeight = retInfo.stackHeight; + if (subInfo.prevInfo != null) + info.jsrInfo = new JsrUsedInfo(subInfo.prevInfo); + else + info.jsrInfo = null; + BitSet usedLocals = subInfo.usedLocals; + for (int j = 0; j < bb.getMaxLocals(); j++) { + if (usedLocals.get(j)) + info.locals[j] = retInfo.locals[j]; + } + if (mergeInfo(nextBlock, info)) + todoSet.set(nextBlock.getBlockNr()); + } else { + beforeJsrs[nextBlock.getBlockNr()] = info; + } + } + } else if (opcode == opc_ret) { + Type retVarType = info.locals[instr.getLocalSlot()]; + if (info.jsrInfo == null || !retVarType.isOfType(tType("R"))) + throw new VerifyException(instr.getDescription()); + Block jsrTarget = retVarType.getJsrTarget(); + BitSet usedLocals = (BitSet) info.jsrInfo.jsrUsed; + for (Block lastTarget = info.jsrInfo.jsrTarget; + jsrTarget != lastTarget; + lastTarget = subInfos[lastTarget.getBlockNr()] + .prevInfo.jsrTarget) { + if (lastTarget == null) + throw new VerifyException("returned to a leaved jsr"); + usedLocals.or(subInfos[lastTarget.getBlockNr()] + .prevInfo.jsrUsed); + } + + SubroutineInfo subInfo = subInfos[jsrTarget.getBlockNr()]; + if (subInfo.retBlock != null && subInfo.retBlock != block) + throw new VerifyException + ("JsrTarget has more than one ret: " + jsrTarget); + subInfo.retInfo = info; + subInfo.usedLocals = usedLocals; + for (int i=0; i < blocks.length; i++) { + if (subInfo.jsrSuccessors.get(i)) { + VerifyInfo afterJsrInfo; + // If this was the first time, copy the info before + // the jsr. + if (subInfo.retBlock == null) { + afterJsrInfo = beforeJsrs[i]; + // Check if the infos are mergeable, + // i.e. the items on the stack match. + // This isn't specified by the virtual + // machine specification, but it would be + // really bad, if we have to support such + // weird jsrs. The decompiler doesn't + // like them! + if (afterJsrInfo.stackHeight + != info.stackHeight) + throw new VerifyException + ("Stack height differ after jsr to: " + + jsrTarget); + for (int k = 0; k < info.stackHeight; k++) { + if (info.stack[i].mergeType + (this, afterJsrInfo.stack[k]) == tNone) + throw new VerifyException + ("Type error while"+ + " merging stacks after jsr"); + } + } else + afterJsrInfo = (VerifyInfo) verifyInfos[i].clone(); + + afterJsrInfo.stack = info.stack; + afterJsrInfo.stackHeight = info.stackHeight; + afterJsrInfo.jsrInfo = subInfo.prevInfo; + if (subInfo.prevInfo != null) + afterJsrInfo.jsrInfo + = new JsrUsedInfo(subInfo.prevInfo); + else + afterJsrInfo.jsrInfo = null; + for (int j = 0; j < bb.getMaxLocals(); j++) { + if (usedLocals.get(j)) + afterJsrInfo.locals[j] = info.locals[j]; + } + if (mergeInfo(blocks[i], afterJsrInfo)) + todoSet.set(i); + } + } + subInfo.retBlock = block; + } else { + Block[] succs = block.getSuccs(); + for (int i=0; i< succs.length; i++) { + Block succ = succs[i]; + if (succ == null) { + if (info.stackHeight != 0) + throw new VerifyException(); + continue; + } + + /* We don't need to check for uninitialized objects + * in back-branch. The reason is the following: + * + * An uninitialized object can't merge with anything + * else, so if this is really a back-branch to already + * analyzed code, the uninitialized object will simply + * vanish to unknown on the merge. + */ + if (mergeInfo(succ, info)) + todoSet.set(succ.getBlockNr()); + } + } + } + + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_VERIFIER) != 0) { + dumpInfo(GlobalOptions.err); + } + } + + public void verify() throws VerifyException { + try { + doVerify(); + } catch (VerifyException ex) { + dumpInfo(GlobalOptions.err); + throw ex; + } catch (RuntimeException ex) { + dumpInfo(GlobalOptions.err); + throw ex; + } + } +} diff --git a/jode/jode/jvm/CodeVerifier.java.in b/jode/jode/jvm/CodeVerifier.java.in deleted file mode 100644 index 556902f..0000000 --- a/jode/jode/jvm/CodeVerifier.java.in +++ /dev/null @@ -1,1127 +0,0 @@ -/* 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: - *
"N...;"
- *
The new instruction, or null if this is the this param - * of <init>.
- *
"R"
The target 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: - *
"[*"
array of something
- *
"+"
(uninitialized) object/returnvalue type
- *
"2", "R"
as the typeSig parameter
- *
- * @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++; - } - - if (c1 == '[' || c2 == '[') { - // Only one of them is array now, the other must be an - // object, the common super is tObject - if (c1 == 'L' || c2 == 'L') { - 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()); - } - return tNone; - } - - 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()); - } - 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")) - 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("")) { - 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= 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; - } - } -} diff --git a/jode/jode/jvm/Interpreter.java.in b/jode/jode/jvm/Interpreter.java similarity index 93% rename from jode/jode/jvm/Interpreter.java.in rename to jode/jode/jvm/Interpreter.java index afdecde..718385c 100644 --- a/jode/jode/jvm/Interpreter.java.in +++ b/jode/jode/jvm/Interpreter.java @@ -20,7 +20,8 @@ package jode.jvm; import jode.AssertError; import jode.GlobalOptions; -import jode.bytecode.BytecodeInfo; +import jode.bytecode.BasicBlocks; +import jode.bytecode.Block; import jode.bytecode.Handler; import jode.bytecode.Instruction; import jode.bytecode.Opcodes; @@ -29,7 +30,10 @@ import jode.bytecode.TypeSignature; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; -import @COLLECTIONS@.Arrays; +///#def COLLECTIONS java.util +import java.util.Arrays; +import java.util.Iterator; +///#enddef /** * This class is a java virtual machine written in java :-). Well not @@ -59,16 +63,16 @@ public class Interpreter implements Opcodes { this.env = env; } - private Value[] fillParameters(BytecodeInfo code, + private Value[] fillParameters(BasicBlocks bb, Object cls, Object[] params) { - Value[] locals = new Value[code.getMaxLocals()]; + Value[] locals = new Value[bb.getMaxLocals()]; for (int i=0; i< locals.length; i++) locals[i] = new Value(); - String myType = code.getMethodInfo().getType(); + String myType = bb.getMethodInfo().getType(); String[] myParamTypes = TypeSignature.getParameterTypes(myType); int slot = 0; - if (!code.getMethodInfo().isStatic()) + if (!bb.getMethodInfo().isStatic()) locals[slot++].setObject(cls); for (int i=0; i< myParamTypes.length; i++) { locals[slot].setObject(params[i]); @@ -77,24 +81,39 @@ public class Interpreter implements Opcodes { return locals; } - public Object interpretMethod(BytecodeInfo code, + public Object interpretMethod(BasicBlocks bb, Object instance, Object[] myParams) throws InterpreterException, InvocationTargetException { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INTERPRT) != 0) - GlobalOptions.err.println("Interpreting "+code); + GlobalOptions.err.println("Interpreting "+bb); - Value[] locals = fillParameters(code, instance, myParams); - Value[] stack = new Value[code.getMaxStack()]; + Value[] locals = fillParameters(bb, instance, myParams); + Value[] stack = new Value[bb.getMaxStack()]; for (int i=0; i < stack.length; i++) stack[i] = new Value(); - Instruction pc = (Instruction) code.getInstructions().get(0); + Block[] blocks = bb.getBlocks(); + Block nextBlock = bb.getStartBlock(); + int stacktop = 0; + Block[] succs = null; + Handler[] handlers = null; + Iterator iter = null; + big_loop: for(;;) { + if (iter == null || !iter.hasNext()) { + /* If block is over continue with the next block */ + if (nextBlock == null) + return Void.TYPE; + iter = nextBlock.getInstructions().iterator(); + succs = nextBlock.getSuccs(); + handlers = nextBlock.getCatchers(); + nextBlock = succs.length > 0 ? succs[succs.length - 1] : null; + } try { - Instruction instr = pc; + Instruction instr = (Instruction) iter.next(); if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INTERPRT) != 0) { GlobalOptions.err.println(instr.getDescription()); @@ -114,7 +133,6 @@ public class Interpreter implements Opcodes { GlobalOptions.err.print(locals[i]+","); GlobalOptions.err.println("]"); } - pc = instr.getNextByAddr(); int opcode = instr.getOpcode(); switch (opcode) { case opc_nop: @@ -572,28 +590,26 @@ public class Interpreter implements Opcodes { int opc_mask = 1 << opcode; if (value > 0 && (opc_mask & CMP_GREATER_MASK) != 0 || value < 0 && (opc_mask & CMP_LESS_MASK) != 0 - || value == 0 && (opc_mask & CMP_EQUAL_MASK) != 0) - pc = instr.getSingleSucc(); + || value == 0 && (opc_mask & CMP_EQUAL_MASK) != 0) { + nextBlock = succs[0]; + } break; } case opc_jsr: case opc_jsr_w: - stack[stacktop++].setObject(instr); - /* fall through */ - case opc_goto: - case opc_goto_w: - pc = instr.getSingleSucc(); + stack[stacktop++].setObject(nextBlock); + nextBlock = succs[0]; break; case opc_ret: - pc = (Instruction)locals[instr.getLocalSlot()].objectValue(); + nextBlock + = (Block) locals[instr.getLocalSlot()].objectValue(); break; case opc_lookupswitch: { int value = stack[--stacktop].intValue(); int[] values = instr.getValues(); int pos = Arrays.binarySearch(values, value); - pc = pos < 0 - ? instr.getSuccs()[values.length] - : instr.getSuccs()[pos]; + if (pos >= 0) + nextBlock = succs[pos]; break; } case opc_ireturn: case opc_freturn: case opc_areturn: @@ -734,16 +750,14 @@ public class Interpreter implements Opcodes { throw new AssertError("Invalid opcode "+opcode); } } catch (InvocationTargetException ex) { - Handler[] handlers = code.getExceptionHandlers(); + iter = null; Throwable obj = ex.getTargetException(); - for (int i=0; i< handlers.length; i++) { - if (handlers[i].start.compareTo(pc) <= 0 - && handlers[i].end.compareTo(pc) >= 0 - && (handlers[i].type == null - || env.instanceOf(obj, handlers[i].type))) { + for (int i=0; i < handlers.length; i++) { + if (handlers[i].getType() == null + || env.instanceOf(obj, handlers[i].getType())) { stacktop = 0; stack[stacktop++].setObject(obj); - pc = handlers[i].catcher; + nextBlock = handlers[i].getCatcher(); continue big_loop; } } diff --git a/jode/jode/jvm/Makefile.am b/jode/jode/jvm/Makefile.am index 2f2372b..f73feab 100644 --- a/jode/jode/jvm/Makefile.am +++ b/jode/jode/jvm/Makefile.am @@ -1,10 +1,8 @@ ## Input file for automake to generate the Makefile.in used by configure -JAR = @JAR@ -JAVAC = @JAVAC@ -JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ - -dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \ - -depfile=Makefile.dep +JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \ + -subdir=$(subdir) -dependdir=$(top_builddir) \ + -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep CLASSPATH = @CLASSPATH@ CLASSLIB = @CLASSLIB@ SUBSTCP = @SUBSTCP@ diff --git a/jode/jode/jvm/SyntheticAnalyzer.java.in b/jode/jode/jvm/SyntheticAnalyzer.java similarity index 69% rename from jode/jode/jvm/SyntheticAnalyzer.java.in rename to jode/jode/jvm/SyntheticAnalyzer.java index 4e55ef4..b5c738a 100644 --- a/jode/jode/jvm/SyntheticAnalyzer.java.in +++ b/jode/jode/jvm/SyntheticAnalyzer.java @@ -19,7 +19,8 @@ package jode.jvm; import jode.GlobalOptions; -import jode.bytecode.BytecodeInfo; +import jode.bytecode.BasicBlocks; +import jode.bytecode.Block; import jode.bytecode.ClassInfo; import jode.bytecode.FieldInfo; import jode.bytecode.Handler; @@ -32,7 +33,9 @@ import jode.type.MethodType; import java.lang.reflect.Modifier; -import @COLLECTIONS@.Iterator; +///#def COLLECTIONS java.util +import java.util.Iterator; +///#enddef public class SyntheticAnalyzer implements Opcodes { public final static int UNKNOWN = 0; @@ -47,11 +50,14 @@ public class SyntheticAnalyzer implements Opcodes { int kind = UNKNOWN; Reference reference; + ClassInfo classInfo; MethodInfo method; - public SyntheticAnalyzer(MethodInfo method, boolean checkName) { + public SyntheticAnalyzer(ClassInfo classInfo, MethodInfo method, + boolean checkName) { + this.classInfo = classInfo; this.method = method; - if (method.getBytecode() == null) + if (method.getBasicBlocks() == null) return; if (!checkName || method.getName().equals("class$")) if (checkGetClass()) @@ -94,37 +100,50 @@ public class SyntheticAnalyzer implements Opcodes { .equals("(Ljava/lang/String;)Ljava/lang/Class;"))) return false; - BytecodeInfo bytecode = method.getBytecode(); + BasicBlocks bb = method.getBasicBlocks(); - Handler[] excHandlers = bytecode.getExceptionHandlers(); - if (excHandlers.length != 1 - || !"java.lang.ClassNotFoundException".equals(excHandlers[0].type)) + Block[] blocks = bb.getBlocks(); + Block startBlock = bb.getStartBlock(); + Handler[] excHandlers = bb.getExceptionHandlers(); + if (startBlock == null + || startBlock.getInstructions().size() != 3 + || excHandlers.length != 1 + || excHandlers[0].getStart() != startBlock + || excHandlers[0].getEnd() != startBlock + || !"java.lang.ClassNotFoundException" + .equals(excHandlers[0].getType())) return false; - int excSlot = -1; - int i = 0; - for (Iterator iter = bytecode.getInstructions().iterator(); iter.hasNext(); i++) { - Instruction instr = (Instruction) iter.next(); - if (i == getClassOpcodes.length - || instr.getOpcode() != getClassOpcodes[i]) + for (int i=0; i< 3; i++) { + Instruction instr = + (Instruction) startBlock.getInstructions().get(i); + if (instr.getOpcode() != getClassOpcodes[i]) return false; - if (i == 0 && (instr.getLocalSlot() != 0 - || excHandlers[0].start != instr)) + if (getClassRefs[i] != null + && !getClassRefs[i].equals(instr.getReference())) return false; - if (i == 2 && excHandlers[0].end != instr) + if (i == 0 && instr.getLocalSlot() != 0) return false; - if (i == 3) { - if (excHandlers[0].catcher != instr) - return false; + } + + Block catchBlock = excHandlers[0].getCatcher(); + if (catchBlock.getInstructions().size() != 7) + return false; + int excSlot = -1; + for (int i=0; i< 7; i++) { + Instruction instr = (Instruction) + catchBlock.getInstructions().get(i); + if (instr.getOpcode() != getClassOpcodes[3+i]) + return false; + if (getClassRefs[3+i] != null + && !getClassRefs[3+i].equals(instr.getReference())) + return false; + if (i == 0) excSlot = instr.getLocalSlot(); - } - if (i == 4 && !instr.getClazzType().equals + if (i == 1 && !instr.getClazzType().equals ("Ljava/lang/NoClassDefFoundError;")) return false; - if (i == 6 && instr.getLocalSlot() != excSlot) - return false; - if (getClassRefs[i] != null - && !getClassRefs[i].equals(instr.getReference())) + if (i == 3 && instr.getLocalSlot() != excSlot) return false; } this.kind = GETCLASS; @@ -135,22 +154,35 @@ public class SyntheticAnalyzer implements Opcodes { Modifier.PUBLIC | Modifier.STATIC); public boolean checkStaticAccess() { - ClassInfo clazzInfo = method.getClazzInfo(); - BytecodeInfo bytecode = method.getBytecode(); - Iterator iter = bytecode.getInstructions().iterator(); - + BasicBlocks bb = method.getBasicBlocks(); + Handler[] excHandlers = bb.getExceptionHandlers(); + if (excHandlers != null && excHandlers.length != 0) + return false; + Block[] blocks = bb.getBlocks(); + Block startBlock = bb.getStartBlock(); + if (startBlock == null) + return false; + Block[] succBlocks = startBlock.getSuccs(); + if (succBlocks.length > 1 || + (succBlocks.length == 1 && succBlocks[0] != null)) + return false; + Iterator iter = startBlock.getInstructions().iterator(); + if (!iter.hasNext()) + return false; Instruction instr = (Instruction) iter.next(); if (instr.getOpcode() == opc_getstatic) { Reference ref = instr.getReference(); String refClazz = ref.getClazz().substring(1); if (!(refClazz.substring(0, refClazz.length()-1) - .equals(clazzInfo.getName().replace('.','/')))) + .equals(classInfo.getName().replace('.','/')))) return false; FieldInfo refField - = clazzInfo.findField(ref.getName(), ref.getType()); + = classInfo.findField(ref.getName(), ref.getType()); if ((refField.getModifiers() & modifierMask) != (Modifier.PRIVATE | Modifier.STATIC)) return false; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) @@ -167,6 +199,8 @@ public class SyntheticAnalyzer implements Opcodes { params++; slot += (instr.getOpcode() == opc_lload || instr.getOpcode() == opc_dload) ? 2 : 1; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); } if (instr.getOpcode() == opc_putstatic) { @@ -176,13 +210,15 @@ public class SyntheticAnalyzer implements Opcodes { Reference ref = instr.getReference(); String refClazz = ref.getClazz().substring(1); if (!(refClazz.substring(0, refClazz.length()-1) - .equals(clazzInfo.getName().replace('.','/')))) + .equals(classInfo.getName().replace('.','/')))) return false; FieldInfo refField - = clazzInfo.findField(ref.getName(), ref.getType()); + = classInfo.findField(ref.getName(), ref.getType()); if ((refField.getModifiers() & modifierMask) != (Modifier.PRIVATE | Modifier.STATIC)) return false; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); if (instr.getOpcode() != opc_return) return false; @@ -194,20 +230,25 @@ public class SyntheticAnalyzer implements Opcodes { Reference ref = instr.getReference(); String refClazz = ref.getClazz().substring(1); if (!(refClazz.substring(0, refClazz.length()-1) - .equals(clazzInfo.getName().replace('.','/')))) + .equals(classInfo.getName().replace('.','/')))) return false; MethodInfo refMethod - = clazzInfo.findMethod(ref.getName(), ref.getType()); + = classInfo.findMethod(ref.getName(), ref.getType()); MethodType refType = Type.tMethod(ref.getType()); if ((refMethod.getModifiers() & modifierMask) != (Modifier.PRIVATE | Modifier.STATIC) || refType.getParameterTypes().length != params) return false; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); if (refType.getReturnType() == Type.tVoid) { - if (instr.getOpcode() != opc_return) + if (iter.hasNext()) return false; } else { + if (!iter.hasNext()) + return false; + instr = (Instruction) iter.next(); if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) return false; @@ -222,33 +263,46 @@ public class SyntheticAnalyzer implements Opcodes { } public boolean checkAccess() { - ClassInfo clazzInfo = method.getClazzInfo(); - BytecodeInfo bytecode = method.getBytecode(); - Handler[] excHandlers = bytecode.getExceptionHandlers(); - if (excHandlers != null && excHandlers.length != 0) - return false; - if (method.isStatic()) { if (checkStaticAccess()) return true; } - Iterator iter = bytecode.getInstructions().iterator(); + BasicBlocks bb = method.getBasicBlocks(); + Handler[] excHandlers = bb.getExceptionHandlers(); + if (excHandlers != null && excHandlers.length != 0) + return false; + Block[] blocks = bb.getBlocks(); + Block startBlock = bb.getStartBlock(); + if (startBlock == null) + return false; + Block[] succBlocks = startBlock.getSuccs(); + if (succBlocks.length > 1 || + (succBlocks.length == 1 && succBlocks[0] != null)) + return false; + Iterator iter = startBlock.getInstructions().iterator(); + + if (!iter.hasNext()) + return false; Instruction instr = (Instruction) iter.next(); if (instr.getOpcode() != opc_aload || instr.getLocalSlot() != 0) return false; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); if (instr.getOpcode() == opc_getfield) { Reference ref = instr.getReference(); String refClazz = ref.getClazz().substring(1); if (!(refClazz.substring(0, refClazz.length()-1) - .equals(clazzInfo.getName().replace('.','/')))) + .equals(classInfo.getName().replace('.','/')))) return false; FieldInfo refField - = clazzInfo.findField(ref.getName(), ref.getType()); + = classInfo.findField(ref.getName(), ref.getType()); if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE) return false; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) @@ -265,6 +319,8 @@ public class SyntheticAnalyzer implements Opcodes { params++; slot += (instr.getOpcode() == opc_lload || instr.getOpcode() == opc_dload) ? 2 : 1; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); } if (instr.getOpcode() == opc_putfield) { @@ -274,14 +330,13 @@ public class SyntheticAnalyzer implements Opcodes { Reference ref = instr.getReference(); String refClazz = ref.getClazz().substring(1); if (!(refClazz.substring(0, refClazz.length()-1) - .equals(clazzInfo.getName().replace('.','/')))) + .equals(classInfo.getName().replace('.','/')))) return false; FieldInfo refField - = clazzInfo.findField(ref.getName(), ref.getType()); + = classInfo.findField(ref.getName(), ref.getType()); if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE) return false; - instr = (Instruction) iter.next(); - if (instr.getOpcode() != opc_return) + if (iter.hasNext()) return false; reference = ref; kind = ACCESSPUTFIELD; @@ -291,19 +346,21 @@ public class SyntheticAnalyzer implements Opcodes { Reference ref = instr.getReference(); String refClazz = ref.getClazz().substring(1); if (!(refClazz.substring(0, refClazz.length()-1) - .equals(clazzInfo.getName().replace('.','/')))) + .equals(classInfo.getName().replace('.','/')))) return false; MethodInfo refMethod - = clazzInfo.findMethod(ref.getName(), ref.getType()); + = classInfo.findMethod(ref.getName(), ref.getType()); MethodType refType = Type.tMethod(ref.getType()); if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE || refType.getParameterTypes().length != params) return false; - instr = (Instruction) iter.next(); if (refType.getReturnType() == Type.tVoid) { - if (instr.getOpcode() != opc_return) + if (iter.hasNext()) return false; } else { + if (!iter.hasNext()) + return false; + instr = (Instruction) iter.next(); if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) return false; @@ -318,16 +375,25 @@ public class SyntheticAnalyzer implements Opcodes { } public boolean checkConstructorAccess() { - ClassInfo clazzInfo = method.getClazzInfo(); - BytecodeInfo bytecode = method.getBytecode(); - Handler[] excHandlers = bytecode.getExceptionHandlers(); + BasicBlocks bb = method.getBasicBlocks(); + Handler[] excHandlers = bb.getExceptionHandlers(); if (excHandlers != null && excHandlers.length != 0) return false; - - Iterator iter = bytecode.getInstructions().iterator(); + Block[] blocks = bb.getBlocks(); + Block startBlock = bb.getStartBlock(); + if (startBlock == null) + return false; + Block[] succBlocks = startBlock.getSuccs(); + if (succBlocks.length != 1 || succBlocks[0] != null) + return false; + Iterator iter = startBlock.getInstructions().iterator(); + if (!iter.hasNext()) + return false; Instruction instr = (Instruction) iter.next(); if (instr.getOpcode() != opc_aload || instr.getLocalSlot() != 0) return false; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); // slot begins with 2. Slot 1 contains a dummy value, that @@ -339,25 +405,25 @@ public class SyntheticAnalyzer implements Opcodes { params++; slot += (instr.getOpcode() == opc_lload || instr.getOpcode() == opc_dload) ? 2 : 1; + if (!iter.hasNext()) + return false; instr = (Instruction) iter.next(); } if (instr.getOpcode() == opc_invokespecial) { Reference ref = instr.getReference(); String refClazz = ref.getClazz().substring(1); if (!(refClazz.substring(0, refClazz.length()-1) - .equals(clazzInfo.getName().replace('.','/')))) + .equals(classInfo.getName().replace('.','/')))) return false; MethodInfo refMethod - = clazzInfo.findMethod(ref.getName(), ref.getType()); + = classInfo.findMethod(ref.getName(), ref.getType()); MethodType refType = Type.tMethod(ref.getType()); if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE || !refMethod.getName().equals("") || refType.getParameterTypes().length != params) return false; - instr = (Instruction) iter.next(); - if (instr.getOpcode() != opc_return) + if (iter.hasNext()) return false; - /* For valid bytecode the types matches automatically */ reference = ref; kind = ACCESSCONSTRUCTOR;