diff --git a/jode/jode/jvm/SyntheticAnalyzer.java b/jode/jode/jvm/SyntheticAnalyzer.java index 990983d..ac05641 100644 --- a/jode/jode/jvm/SyntheticAnalyzer.java +++ b/jode/jode/jvm/SyntheticAnalyzer.java @@ -17,82 +17,287 @@ * $Id$ */ -package jode.decompiler; +package jode.jvm; import jode.GlobalOptions; -import jode.flow.*; -import jode.expr.*; import jode.type.Type; import jode.type.MethodType; +import java.lang.reflect.Modifier; +import jode.bytecode.*; -public class SyntheticAnalyzer { +public class SyntheticAnalyzer implements jode.bytecode.Opcodes { public final static int UNKNOWN = 0; public final static int GETCLASS = 1; - public final static int GETFIELD = 2; - public final static int PUTFIELD = 3; + public final static int ACCESSGETFIELD = 2; + public final static int ACCESSPUTFIELD = 3; + public final static int ACCESSMETHOD = 4; + public final static int ACCESSGETSTATIC = 5; + public final static int ACCESSPUTSTATIC = 6; + public final static int ACCESSSTATICMETHOD = 7; - int type = UNKNOWN; - MethodAnalyzer method; + int kind = UNKNOWN; + Reference reference; + MethodInfo method; - public SyntheticAnalyzer(MethodAnalyzer method) { + public SyntheticAnalyzer(MethodInfo method, boolean checkName) { this.method = method; - if (method.getName().equals("class$")) - if (!checkGetClass() && GlobalOptions.verboseLevel > 0) - GlobalOptions.err.println("class$ seems to be wrong"); + if (method.getBytecode() == null) + return; + if (!checkName || method.getName().equals("class$")) + if (checkGetClass()) + return; + if (!checkName || method.getName().startsWith("access$")) + if (checkAccess()) + return; } + public int getKind() { + return kind; + } + + public Reference getReference() { + return reference; + } + + private static final int[] getClassOpcodes = { + opc_aload, opc_invokestatic, opc_areturn, + opc_astore, opc_new, opc_dup, opc_aload, + opc_invokevirtual, opc_invokespecial, opc_athrow + }; + private static final Object[] getClassRefs = { + null, Reference.getReference("Ljava/lang/Class;", "forName", + "(Ljava/lang/String;)Ljava/lang/Class;"), + null, null, "Ljava/lang/NoClassDefFoundError;", null, null, + Reference.getReference("Ljava/lang/Throwable;", "getMessage", + "()Ljava/lang/String;"), + Reference.getReference("Ljava/lang/NoClassDefFoundError;", "", + "(Ljava/lang/String;)V"), null + }; + + boolean checkGetClass() { - MethodType type = method.getType(); if (!method.isStatic() - || !type.getReturnType().isOfType(Type.tJavaLangClass) - || type.getParameterTypes().length != 1 - || !type.getParameterTypes()[0].isOfType(Type.tString)) + || !(method.getType() + .equals("(Ljava/lang/String;)Ljava/lang/Class;"))) return false; - FlowBlock flow = method.getMethodHeader(); - if (!flow.hasNoJumps()) - return false; - StructuredBlock tryblock = flow.getBlock(); - if (!(tryblock instanceof TryBlock)) - return false; - StructuredBlock[] subBlocks = tryblock.getSubBlocks(); - if (subBlocks.length != 2 - || !(subBlocks[0] instanceof ReturnBlock) - || !(subBlocks[1] instanceof CatchBlock)) + BytecodeInfo bytecode = method.getBytecode(); + + Handler[] excHandlers = bytecode.getExceptionHandlers(); + if (excHandlers.length != 1) return false; - // Now check the return Block, it should be - // return Class.forName(local_0); - ReturnBlock ret = (ReturnBlock) subBlocks[0]; - Expression retExpr = ret.getInstruction(); - if (!(retExpr instanceof ComplexExpression) - || !(retExpr.getOperator() instanceof InvokeOperator)) + Instruction instr = bytecode.getFirstInstr(); + if (excHandlers[0].start != instr) return false; - InvokeOperator invoke = (InvokeOperator) retExpr.getOperator(); - if (!invoke.isStatic() - || !invoke.getClassType().equals(Type.tJavaLangClass) - || !(invoke.getMethodType().getReturnType() - .equals(Type.tJavaLangClass)) - || invoke.getMethodType().getParameterTypes().length != 1 - || !(invoke.getMethodType().getParameterTypes()[0] - .equals(Type.tString)) - || !invoke.getMethodName().equals("forName")) + for (int i=0; i< getClassOpcodes.length; i++) { + if (instr.opcode != getClassOpcodes[i]) + return false; + if (i==0 && instr.localSlot != 0) + return false; + if ((i == 3 || i == 6) && instr.localSlot != 1) + return false; + if (i == 2 && excHandlers[0].end != instr) + return false; + if (i == 3 && excHandlers[0].catcher != instr) + return false; + if (getClassRefs[i] != null + && !getClassRefs[i].equals(instr.objData)) + return false; + instr = instr.nextByAddr; + } + if (instr != null) return false; + this.kind = GETCLASS; + return true; + } + + private final int modifierMask = (Modifier.PRIVATE | Modifier.PROTECTED | + Modifier.PUBLIC | Modifier.STATIC); + + public boolean checkStaticAccess() { + ClassInfo clazzInfo = method.getClazzInfo(); + BytecodeInfo bytecode = method.getBytecode(); + Instruction instr = bytecode.getFirstInstr(); + + if (instr.opcode == opc_getstatic) { + Reference ref = (Reference) instr.objData; + String refClazz = ref.getClazz().substring(1); + if (!(refClazz.substring(0, refClazz.length()-1) + .equals(clazzInfo.getName().replace('.','/')))) + return false; + FieldInfo refField + = clazzInfo.findField(ref.getName(), ref.getType()); + if ((refField.getModifiers() & modifierMask) != + (Modifier.PRIVATE | Modifier.STATIC)) + return false; + instr = instr.nextByAddr; + if (instr.opcode < opc_ireturn || instr.opcode > opc_areturn) + return false; + if (instr.nextByAddr != null) + return false; + /* For valid bytecode the type matches automatically */ + reference = ref; + kind = ACCESSGETSTATIC; + return true; + } + int params = 0, slot = 0; + while (instr.opcode >= opc_iload && instr.opcode <= opc_aload + && instr.localSlot == slot) { + params++; + slot += (instr.opcode == opc_lload + || instr.opcode == opc_dload) ? 2 : 1; + instr = instr.nextByAddr; + } + if (instr.opcode == opc_putstatic) { + if (params != 1) + return false; + /* For valid bytecode the type of param matches automatically */ + Reference ref = (Reference) instr.objData; + String refClazz = ref.getClazz().substring(1); + if (!(refClazz.substring(0, refClazz.length()-1) + .equals(clazzInfo.getName().replace('.','/')))) + return false; + FieldInfo refField + = clazzInfo.findField(ref.getName(), ref.getType()); + if ((refField.getModifiers() & modifierMask) != + (Modifier.PRIVATE | Modifier.STATIC)) + return false; + instr = instr.nextByAddr; + if (instr.opcode != opc_return) + return false; + if (instr.nextByAddr != null) + return false; + reference = ref; + kind = ACCESSPUTSTATIC; + return true; + } + if (instr.opcode == opc_invokestatic) { + Reference ref = (Reference) instr.objData; + String refClazz = ref.getClazz().substring(1); + if (!(refClazz.substring(0, refClazz.length()-1) + .equals(clazzInfo.getName().replace('.','/')))) + return false; + MethodInfo refMethod + = clazzInfo.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; + instr = instr.nextByAddr; + if (refType.getReturnType() == Type.tVoid) { + if (instr.opcode != opc_return) + return false; + } else { + if (instr.opcode < opc_ireturn || instr.opcode > opc_areturn) + return false; + } + if (instr.nextByAddr != null) + return false; + + /* For valid bytecode the types matches automatically */ + reference = ref; + kind = ACCESSSTATICMETHOD; + return true; + } + return false; + } - Expression[] subExpr = - ((ComplexExpression) retExpr).getSubExpressions(); - if (!(subExpr[0] instanceof LocalLoadOperator) - || ((LocalLoadOperator) subExpr[0]).getLocalInfo().getSlot() != 0) + public boolean checkAccess() { + ClassInfo clazzInfo = method.getClazzInfo(); + BytecodeInfo bytecode = method.getBytecode(); + Handler[] excHandlers = bytecode.getExceptionHandlers(); + if (excHandlers != null && excHandlers.length != 0) return false; - // Now check the CatchBlock it should contain (we don't check all): - // throw new NoClassDefFoundError(exception.getMessage()); - CatchBlock catchBlock = (CatchBlock) subBlocks[1]; - StructuredBlock subBlock = catchBlock.getSubBlocks()[0]; - if (!(subBlock instanceof ThrowBlock) - || !(catchBlock.getExceptionType().equals - (Type.tClass("java.lang.ClassNotFoundException")))) + if (method.isStatic()) { + if (checkStaticAccess()) + return true; + } + + Instruction instr = bytecode.getFirstInstr(); + if (instr.opcode != opc_aload || instr.localSlot != 0) return false; - this.type = GETCLASS; - return true; + instr = instr.nextByAddr; + + if (instr.opcode == opc_getfield) { + Reference ref = (Reference) instr.objData; + String refClazz = ref.getClazz().substring(1); + if (!(refClazz.substring(0, refClazz.length()-1) + .equals(clazzInfo.getName().replace('.','/')))) + return false; + FieldInfo refField + = clazzInfo.findField(ref.getName(), ref.getType()); + if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE) + return false; + instr = instr.nextByAddr; + if (instr.opcode < opc_ireturn || instr.opcode > opc_areturn) + return false; + if (instr.nextByAddr != null) + return false; + /* For valid bytecode the type matches automatically */ + reference = ref; + kind = ACCESSGETFIELD; + return true; + } + int params = 0, slot = 1; + while (instr.opcode >= opc_iload && instr.opcode <= opc_aload + && instr.localSlot == slot) { + params++; + slot += (instr.opcode == opc_lload + || instr.opcode == opc_dload) ? 2 : 1; + instr = instr.nextByAddr; + } + if (instr.opcode == opc_putfield) { + if (params != 1) + return false; + /* For valid bytecode the type of param matches automatically */ + Reference ref = (Reference) instr.objData; + String refClazz = ref.getClazz().substring(1); + if (!(refClazz.substring(0, refClazz.length()-1) + .equals(clazzInfo.getName().replace('.','/')))) + return false; + FieldInfo refField + = clazzInfo.findField(ref.getName(), ref.getType()); + if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE) + return false; + instr = instr.nextByAddr; + if (instr.opcode != opc_return) + return false; + if (instr.nextByAddr != null) + return false; + reference = ref; + kind = ACCESSPUTFIELD; + return true; + } + if (instr.opcode == opc_invokespecial) { + Reference ref = (Reference) instr.objData; + String refClazz = ref.getClazz().substring(1); + if (!(refClazz.substring(0, refClazz.length()-1) + .equals(clazzInfo.getName().replace('.','/')))) + return false; + MethodInfo refMethod + = clazzInfo.findMethod(ref.getName(), ref.getType()); + MethodType refType = Type.tMethod(ref.getType()); + if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE + || refType.getParameterTypes().length != params) + return false; + instr = instr.nextByAddr; + if (refType.getReturnType() == Type.tVoid) { + if (instr.opcode != opc_return) + return false; + } else { + if (instr.opcode < opc_ireturn || instr.opcode > opc_areturn) + return false; + } + if (instr.nextByAddr != null) + return false; + + /* For valid bytecode the types matches automatically */ + reference = ref; + kind = ACCESSMETHOD; + return true; + } + return false; } }