/* SyntheticAnalyzer 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.GlobalOptions; import jode.bytecode.BytecodeInfo; import jode.bytecode.ClassInfo; import jode.bytecode.FieldInfo; 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 jode.type.Type; import jode.type.MethodType; import java.lang.reflect.Modifier; import @COLLECTIONS@.Iterator; public class SyntheticAnalyzer implements Opcodes { public final static int UNKNOWN = 0; public final static int GETCLASS = 1; 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; public final static int ACCESSCONSTRUCTOR = 8; public final static int ACCESSDUPPUTFIELD = 9; public final static int ACCESSDUPPUTSTATIC = 10; int kind = UNKNOWN; Reference reference; MethodInfo method; int unifyParam = -1; public SyntheticAnalyzer(MethodInfo method, boolean checkName) { this.method = method; if (method.getBytecode() == null) return; if (!checkName || method.getName().equals("class$")) if (checkGetClass()) return; if (!checkName || method.getName().startsWith("access$")) if (checkAccess()) return; if (method.getName().equals("")) if (checkConstructorAccess()) return; } public int getKind() { return kind; } public Reference getReference() { return reference; } /** * Gets the index of the dummy parameter for an ACCESSCONSTRUCTOR. * Normally the 1 but for inner classes it may be 2. */ public int getUnifyParam() { return unifyParam; } 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 Reference[] getClassRefs = { null, Reference.getReference("Ljava/lang/Class;", "forName", "(Ljava/lang/String;)Ljava/lang/Class;"), null, null, null, null, null, Reference.getReference("Ljava/lang/Throwable;", "getMessage", "()Ljava/lang/String;"), Reference.getReference("Ljava/lang/NoClassDefFoundError;", "", "(Ljava/lang/String;)V"), null }; boolean checkGetClass() { if (!method.isStatic() || !(method.getType() .equals("(Ljava/lang/String;)Ljava/lang/Class;"))) return false; BytecodeInfo bytecode = method.getBytecode(); Handler[] excHandlers = bytecode.getExceptionHandlers(); if (excHandlers.length != 1 || !"java.lang.ClassNotFoundException".equals(excHandlers[0].type)) 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]) return false; if (i == 0 && (instr.getLocalSlot() != 0 || excHandlers[0].start != instr)) return false; if (i == 2 && excHandlers[0].end != instr) return false; if (i == 3) { if (excHandlers[0].catcher != instr) return false; excSlot = instr.getLocalSlot(); } if (i == 4 && !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())) return false; } this.kind = GETCLASS; return true; } private final int modifierMask = Modifier.PUBLIC | Modifier.STATIC; public boolean checkStaticAccess() { ClassInfo clazzInfo = method.getClazzInfo(); BytecodeInfo bytecode = method.getBytecode(); Iterator iter = bytecode.getInstructions().iterator(); boolean dupSeen = false; Instruction instr = (Instruction) iter.next(); if (instr.getOpcode() == opc_getstatic) { Reference ref = instr.getReference(); ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz()); if (!refClazz.superClassOf(clazzInfo)) return false; FieldInfo refField = refClazz.findField(ref.getName(), ref.getType()); if ((refField.getModifiers() & modifierMask) != Modifier.STATIC) return false; instr = (Instruction) iter.next(); if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) return false; /* For valid bytecode the type matches automatically */ reference = ref; kind = ACCESSGETSTATIC; return true; } int params = 0, slot = 0; while (instr.getOpcode() >= opc_iload && instr.getOpcode() <= opc_aload && instr.getLocalSlot() == slot) { params++; slot += (instr.getOpcode() == opc_lload || instr.getOpcode() == opc_dload) ? 2 : 1; instr = (Instruction) iter.next(); } if (instr.getOpcode() == (opc_dup - 3) + 3 * slot) { /* This is probably a opc_dup or opc_dup2, * preceding a opc_putstatic */ instr = (Instruction) iter.next(); if (instr.getOpcode() != opc_putstatic) return false; dupSeen = true; } if (instr.getOpcode() == opc_putstatic) { if (params != 1) return false; /* For valid bytecode the type of param matches automatically */ Reference ref = instr.getReference(); ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz()); if (!refClazz.superClassOf(clazzInfo)) return false; FieldInfo refField = refClazz.findField(ref.getName(), ref.getType()); if ((refField.getModifiers() & modifierMask) != Modifier.STATIC) return false; instr = (Instruction) iter.next(); if (dupSeen) { if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) return false; kind = ACCESSDUPPUTSTATIC; } else { if (instr.getOpcode() != opc_return) return false; kind = ACCESSPUTSTATIC; } reference = ref; return true; } if (instr.getOpcode() == opc_invokestatic) { Reference ref = instr.getReference(); ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz()); if (!refClazz.superClassOf(clazzInfo)) return false; MethodInfo refMethod = refClazz.findMethod(ref.getName(), ref.getType()); MethodType refType = Type.tMethod(ref.getType()); if ((refMethod.getModifiers() & modifierMask) != Modifier.STATIC || refType.getParameterTypes().length != params) return false; instr = (Instruction) iter.next(); if (refType.getReturnType() == Type.tVoid) { if (instr.getOpcode() != opc_return) return false; } else { if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) return false; } /* For valid bytecode the types matches automatically */ reference = ref; kind = ACCESSSTATICMETHOD; return true; } return false; } public boolean checkAccess() { ClassInfo clazzInfo = method.getClazzInfo(); BytecodeInfo bytecode = method.getBytecode(); Handler[] excHandlers = bytecode.getExceptionHandlers(); boolean dupSeen = false; if (excHandlers != null && excHandlers.length != 0) return false; if (method.isStatic()) { if (checkStaticAccess()) return true; } Iterator iter = bytecode.getInstructions().iterator(); Instruction instr = (Instruction) iter.next(); if (instr.getOpcode() != opc_aload || instr.getLocalSlot() != 0) return false; instr = (Instruction) iter.next(); if (instr.getOpcode() == opc_getfield) { Reference ref = instr.getReference(); ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz()); if (!refClazz.superClassOf(clazzInfo)) return false; FieldInfo refField = refClazz.findField(ref.getName(), ref.getType()); if ((refField.getModifiers() & modifierMask) != 0) return false; instr = (Instruction) iter.next(); if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) return false; /* For valid bytecode the type matches automatically */ reference = ref; kind = ACCESSGETFIELD; return true; } int params = 0, slot = 1; while (instr.getOpcode() >= opc_iload && instr.getOpcode() <= opc_aload && instr.getLocalSlot() == slot) { params++; slot += (instr.getOpcode() == opc_lload || instr.getOpcode() == opc_dload) ? 2 : 1; instr = (Instruction) iter.next(); } if (instr.getOpcode() == (opc_dup_x1 - 6) + 3 * slot) { /* This is probably a opc_dup_x1 or opc_dup2_x1, * preceding a opc_putfield */ instr = (Instruction) iter.next(); if (instr.getOpcode() != opc_putfield) return false; dupSeen = true; } if (instr.getOpcode() == opc_putfield) { if (params != 1) return false; /* For valid bytecode the type of param matches automatically */ Reference ref = instr.getReference(); ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz()); if (!refClazz.superClassOf(clazzInfo)) return false; FieldInfo refField = refClazz.findField(ref.getName(), ref.getType()); if ((refField.getModifiers() & modifierMask) != 0) return false; instr = (Instruction) iter.next(); if (dupSeen) { if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) return false; kind = ACCESSDUPPUTFIELD; } else { if (instr.getOpcode() != opc_return) return false; kind = ACCESSPUTFIELD; } reference = ref; return true; } if (instr.getOpcode() == opc_invokespecial) { Reference ref = instr.getReference(); ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz()); if (!refClazz.superClassOf(clazzInfo)) return false; MethodInfo refMethod = refClazz.findMethod(ref.getName(), ref.getType()); MethodType refType = Type.tMethod(ref.getType()); if ((refMethod.getModifiers() & modifierMask) != 0 || refType.getParameterTypes().length != params) return false; instr = (Instruction) iter.next(); if (refType.getReturnType() == Type.tVoid) { if (instr.getOpcode() != opc_return) return false; } else { if (instr.getOpcode() < opc_ireturn || instr.getOpcode() > opc_areturn) return false; } /* For valid bytecode the types matches automatically */ reference = ref; kind = ACCESSMETHOD; return true; } return false; } public boolean checkConstructorAccess() { ClassInfo clazzInfo = method.getClazzInfo(); BytecodeInfo bytecode = method.getBytecode(); String[] paramTypes = TypeSignature.getParameterTypes(method.getType()); Handler[] excHandlers = bytecode.getExceptionHandlers(); if (excHandlers != null && excHandlers.length != 0) return false; Iterator iter = bytecode.getInstructions().iterator(); Instruction instr = (Instruction) iter.next(); int params = 0, slot = 0; while (instr.getOpcode() >= opc_iload && instr.getOpcode() <= opc_aload) { if (instr.getLocalSlot() > slot && unifyParam == -1 && params > 0 && paramTypes[params - 1].charAt(0) == 'L') { unifyParam = params; params++; slot++; } if (instr.getLocalSlot() != slot) return false; params++; slot += (instr.getOpcode() == opc_lload || instr.getOpcode() == opc_dload) ? 2 : 1; instr = (Instruction) iter.next(); } if (params > 0 && instr.getOpcode() == opc_invokespecial) { if (unifyParam == -1 && params <= paramTypes.length && paramTypes[params - 1].charAt(0) == 'L') unifyParam = params++; Reference ref = instr.getReference(); ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz()); if (refClazz != clazzInfo) return false; MethodInfo refMethod = refClazz.findMethod(ref.getName(), ref.getType()); MethodType refType = Type.tMethod(ref.getType()); if ((refMethod.getModifiers() & modifierMask) != 0 || !refMethod.getName().equals("") || unifyParam == -1 || refType.getParameterTypes().length != params - 2) return false; instr = (Instruction) iter.next(); if (instr.getOpcode() != opc_return) return false; /* We don't check if types matches. No problem since we only * need to make sure, this constructor doesn't do anything * more than relay to the real one. */ reference = ref; kind = ACCESSCONSTRUCTOR; return true; } return false; } }