|
|
@ -17,82 +17,287 @@ |
|
|
|
* $Id$ |
|
|
|
* $Id$ |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
package jode.decompiler; |
|
|
|
package jode.jvm; |
|
|
|
import jode.GlobalOptions; |
|
|
|
import jode.GlobalOptions; |
|
|
|
import jode.flow.*; |
|
|
|
|
|
|
|
import jode.expr.*; |
|
|
|
|
|
|
|
import jode.type.Type; |
|
|
|
import jode.type.Type; |
|
|
|
import jode.type.MethodType; |
|
|
|
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 UNKNOWN = 0; |
|
|
|
public final static int GETCLASS = 1; |
|
|
|
public final static int GETCLASS = 1; |
|
|
|
public final static int GETFIELD = 2; |
|
|
|
public final static int ACCESSGETFIELD = 2; |
|
|
|
public final static int PUTFIELD = 3; |
|
|
|
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; |
|
|
|
int kind = UNKNOWN; |
|
|
|
MethodAnalyzer method; |
|
|
|
Reference reference; |
|
|
|
|
|
|
|
MethodInfo method; |
|
|
|
|
|
|
|
|
|
|
|
public SyntheticAnalyzer(MethodAnalyzer method) { |
|
|
|
public SyntheticAnalyzer(MethodInfo method, boolean checkName) { |
|
|
|
this.method = method; |
|
|
|
this.method = method; |
|
|
|
if (method.getName().equals("class$")) |
|
|
|
if (method.getBytecode() == null) |
|
|
|
if (!checkGetClass() && GlobalOptions.verboseLevel > 0) |
|
|
|
return; |
|
|
|
GlobalOptions.err.println("class$ seems to be wrong"); |
|
|
|
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;", "<init>", |
|
|
|
|
|
|
|
"(Ljava/lang/String;)V"), null |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean checkGetClass() { |
|
|
|
boolean checkGetClass() { |
|
|
|
MethodType type = method.getType(); |
|
|
|
|
|
|
|
if (!method.isStatic() |
|
|
|
if (!method.isStatic() |
|
|
|
|| !type.getReturnType().isOfType(Type.tJavaLangClass) |
|
|
|
|| !(method.getType() |
|
|
|
|| type.getParameterTypes().length != 1 |
|
|
|
.equals("(Ljava/lang/String;)Ljava/lang/Class;"))) |
|
|
|
|| !type.getParameterTypes()[0].isOfType(Type.tString)) |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
FlowBlock flow = method.getMethodHeader(); |
|
|
|
BytecodeInfo bytecode = method.getBytecode(); |
|
|
|
if (!flow.hasNoJumps()) |
|
|
|
|
|
|
|
return false; |
|
|
|
Handler[] excHandlers = bytecode.getExceptionHandlers(); |
|
|
|
StructuredBlock tryblock = flow.getBlock(); |
|
|
|
if (excHandlers.length != 1) |
|
|
|
if (!(tryblock instanceof TryBlock)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
StructuredBlock[] subBlocks = tryblock.getSubBlocks(); |
|
|
|
|
|
|
|
if (subBlocks.length != 2 |
|
|
|
|
|
|
|
|| !(subBlocks[0] instanceof ReturnBlock) |
|
|
|
|
|
|
|
|| !(subBlocks[1] instanceof CatchBlock)) |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
// Now check the return Block, it should be
|
|
|
|
Instruction instr = bytecode.getFirstInstr(); |
|
|
|
// return Class.forName(local_0);
|
|
|
|
if (excHandlers[0].start != instr) |
|
|
|
ReturnBlock ret = (ReturnBlock) subBlocks[0]; |
|
|
|
|
|
|
|
Expression retExpr = ret.getInstruction(); |
|
|
|
|
|
|
|
if (!(retExpr instanceof ComplexExpression) |
|
|
|
|
|
|
|
|| !(retExpr.getOperator() instanceof InvokeOperator)) |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
InvokeOperator invoke = (InvokeOperator) retExpr.getOperator(); |
|
|
|
for (int i=0; i< getClassOpcodes.length; i++) { |
|
|
|
if (!invoke.isStatic() |
|
|
|
if (instr.opcode != getClassOpcodes[i]) |
|
|
|
|| !invoke.getClassType().equals(Type.tJavaLangClass) |
|
|
|
return false; |
|
|
|
|| !(invoke.getMethodType().getReturnType() |
|
|
|
if (i==0 && instr.localSlot != 0) |
|
|
|
.equals(Type.tJavaLangClass)) |
|
|
|
return false; |
|
|
|
|| invoke.getMethodType().getParameterTypes().length != 1 |
|
|
|
if ((i == 3 || i == 6) && instr.localSlot != 1) |
|
|
|
|| !(invoke.getMethodType().getParameterTypes()[0] |
|
|
|
return false; |
|
|
|
.equals(Type.tString)) |
|
|
|
if (i == 2 && excHandlers[0].end != instr) |
|
|
|
|| !invoke.getMethodName().equals("forName")) |
|
|
|
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; |
|
|
|
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 = |
|
|
|
public boolean checkAccess() { |
|
|
|
((ComplexExpression) retExpr).getSubExpressions(); |
|
|
|
ClassInfo clazzInfo = method.getClazzInfo(); |
|
|
|
if (!(subExpr[0] instanceof LocalLoadOperator) |
|
|
|
BytecodeInfo bytecode = method.getBytecode(); |
|
|
|
|| ((LocalLoadOperator) subExpr[0]).getLocalInfo().getSlot() != 0) |
|
|
|
Handler[] excHandlers = bytecode.getExceptionHandlers(); |
|
|
|
|
|
|
|
if (excHandlers != null && excHandlers.length != 0) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
// Now check the CatchBlock it should contain (we don't check all):
|
|
|
|
if (method.isStatic()) { |
|
|
|
// throw new NoClassDefFoundError(exception.getMessage());
|
|
|
|
if (checkStaticAccess()) |
|
|
|
CatchBlock catchBlock = (CatchBlock) subBlocks[1]; |
|
|
|
return true; |
|
|
|
StructuredBlock subBlock = catchBlock.getSubBlocks()[0]; |
|
|
|
} |
|
|
|
if (!(subBlock instanceof ThrowBlock) |
|
|
|
|
|
|
|
|| !(catchBlock.getExceptionType().equals |
|
|
|
Instruction instr = bytecode.getFirstInstr(); |
|
|
|
(Type.tClass("java.lang.ClassNotFoundException")))) |
|
|
|
if (instr.opcode != opc_aload || instr.localSlot != 0) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
this.type = GETCLASS; |
|
|
|
instr = instr.nextByAddr; |
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|