You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
451 lines
14 KiB
451 lines
14 KiB
/* 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("<init>"))
|
|
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;", "<init>",
|
|
"(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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
instr = (Instruction) iter.next();
|
|
if (instr.getOpcode() != opc_aload || instr.getLocalSlot() != 0)
|
|
return false;
|
|
instr = (Instruction) iter.next();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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();
|
|
while (instr.getOpcode() == opc_nop && iter.hasNext())
|
|
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("<init>")
|
|
|| 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;
|
|
}
|
|
}
|
|
|
|
|