|
|
|
@ -18,7 +18,9 @@ |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
package jode.expr; |
|
|
|
|
import jode.Decompiler; |
|
|
|
|
import jode.decompiler.CodeAnalyzer; |
|
|
|
|
import jode.decompiler.MethodAnalyzer; |
|
|
|
|
import jode.decompiler.ClassAnalyzer; |
|
|
|
|
import jode.decompiler.TabbedPrintWriter; |
|
|
|
|
import jode.GlobalOptions; |
|
|
|
@ -26,6 +28,7 @@ import jode.bytecode.*; |
|
|
|
|
import jode.jvm.*; |
|
|
|
|
import jode.type.*; |
|
|
|
|
import java.lang.reflect.InvocationTargetException; |
|
|
|
|
import jode.decompiler.Scope; |
|
|
|
|
|
|
|
|
|
public final class InvokeOperator extends Operator |
|
|
|
|
implements MatchableOperator { |
|
|
|
@ -34,7 +37,7 @@ public final class InvokeOperator extends Operator |
|
|
|
|
boolean specialFlag; |
|
|
|
|
MethodType methodType; |
|
|
|
|
String methodName; |
|
|
|
|
Type clazz; |
|
|
|
|
Type classType; |
|
|
|
|
|
|
|
|
|
public InvokeOperator(CodeAnalyzer codeAnalyzer, |
|
|
|
|
boolean staticFlag, boolean specialFlag, |
|
|
|
@ -42,13 +45,13 @@ public final class InvokeOperator extends Operator |
|
|
|
|
super(Type.tUnknown, 0); |
|
|
|
|
this.methodType = (MethodType) Type.tType(reference.getType()); |
|
|
|
|
this.methodName = reference.getName(); |
|
|
|
|
this.clazz = Type.tType(reference.getClazz()); |
|
|
|
|
this.classType = Type.tType(reference.getClazz()); |
|
|
|
|
this.type = methodType.getReturnType(); |
|
|
|
|
this.codeAnalyzer = codeAnalyzer; |
|
|
|
|
this.staticFlag = staticFlag; |
|
|
|
|
this.specialFlag = specialFlag; |
|
|
|
|
if (staticFlag) |
|
|
|
|
codeAnalyzer.useType(clazz); |
|
|
|
|
codeAnalyzer.useType(classType); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -83,7 +86,7 @@ public final class InvokeOperator extends Operator |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Type getClassType() { |
|
|
|
|
return clazz; |
|
|
|
|
return classType; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public int getPriority() { |
|
|
|
@ -111,26 +114,89 @@ public final class InvokeOperator extends Operator |
|
|
|
|
return methodName.equals("<init>"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public ClassInfo getClassInfo() { |
|
|
|
|
if (classType instanceof ClassInterfacesType) |
|
|
|
|
return ((ClassInterfacesType) classType).getClassInfo(); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Checks, whether this is a call of a method from this class. |
|
|
|
|
* @XXX check, if this class implements the method and if not |
|
|
|
|
* allow super class
|
|
|
|
|
*/ |
|
|
|
|
public boolean isThis() { |
|
|
|
|
if (clazz instanceof ClassInterfacesType) { |
|
|
|
|
return ((ClassInterfacesType) clazz).getClassInfo() |
|
|
|
|
== codeAnalyzer.getClazz(); |
|
|
|
|
return getClassInfo() == codeAnalyzer.getClazz(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public ClassInfo getOuterClass() { |
|
|
|
|
ClassInfo clazz = getClassInfo(); |
|
|
|
|
if (clazz != null) { |
|
|
|
|
InnerClassInfo[] outers = clazz.getOuterClasses(); |
|
|
|
|
if (outers != null && outers[0].outer != null |
|
|
|
|
&& (Decompiler.options & Decompiler.OPTION_INNER) != 0) |
|
|
|
|
return ClassInfo.forName(outers[0].outer); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Checks, whether this is a call of a method from this class or an |
|
|
|
|
* outer instance. |
|
|
|
|
*/ |
|
|
|
|
public boolean isOuter() { |
|
|
|
|
if (classType instanceof ClassInterfacesType) { |
|
|
|
|
ClassAnalyzer ana = codeAnalyzer.getClassAnalyzer(); |
|
|
|
|
while (true) { |
|
|
|
|
if (((ClassInterfacesType) classType).getClassInfo() |
|
|
|
|
== ana.getClazz()) |
|
|
|
|
return true; |
|
|
|
|
if (ana.getParent() instanceof CodeAnalyzer |
|
|
|
|
&& (Decompiler.options & Decompiler.OPTION_ANON) != 0) |
|
|
|
|
ana = ((CodeAnalyzer) ana.getParent()) |
|
|
|
|
.getClassAnalyzer(); |
|
|
|
|
else if (ana.getParent() instanceof ConstructorOperator |
|
|
|
|
&& (Decompiler.options & Decompiler.OPTION_ANON) != 0) |
|
|
|
|
ana = ((ConstructorOperator) ana.getParent()) |
|
|
|
|
.getCodeAnalyzer().getClassAnalyzer(); |
|
|
|
|
else if (ana.getParent() instanceof ClassAnalyzer |
|
|
|
|
&& (Decompiler.options |
|
|
|
|
& Decompiler.OPTION_INNER) != 0) |
|
|
|
|
ana = (ClassAnalyzer) ana.getParent(); |
|
|
|
|
else |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public MethodAnalyzer getMethodAnalyzer() { |
|
|
|
|
if (classType instanceof ClassInterfacesType) { |
|
|
|
|
ClassAnalyzer ana = codeAnalyzer.getClassAnalyzer(); |
|
|
|
|
while (true) { |
|
|
|
|
if (((ClassInterfacesType) classType).getClassInfo() |
|
|
|
|
== ana.getClazz()) { |
|
|
|
|
return ana.getMethod(methodName, methodType); |
|
|
|
|
} |
|
|
|
|
if (ana.getParent() instanceof MethodAnalyzer) |
|
|
|
|
ana = ((MethodAnalyzer) ana.getParent()) |
|
|
|
|
.getClassAnalyzer(); |
|
|
|
|
else if (ana.getParent() instanceof ClassAnalyzer) |
|
|
|
|
ana = (ClassAnalyzer) ana.getParent(); |
|
|
|
|
else |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Checks, whether this is a call of a method from the super class. |
|
|
|
|
* @XXX check, if its the first super class that implements the method. |
|
|
|
|
*/ |
|
|
|
|
public boolean isSuperOrThis() { |
|
|
|
|
if (clazz instanceof ClassInterfacesType) { |
|
|
|
|
return ((ClassInterfacesType) clazz).getClassInfo() |
|
|
|
|
if (classType instanceof ClassInterfacesType) { |
|
|
|
|
return ((ClassInterfacesType) classType).getClassInfo() |
|
|
|
|
.superClassOf(codeAnalyzer.getClazz()); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
@ -139,16 +205,20 @@ public final class InvokeOperator extends Operator |
|
|
|
|
public void dumpExpression(TabbedPrintWriter writer, |
|
|
|
|
Expression[] operands) |
|
|
|
|
throws java.io.IOException { |
|
|
|
|
boolean opIsThis = |
|
|
|
|
(!staticFlag |
|
|
|
|
&& operands[0] instanceof LocalLoadOperator |
|
|
|
|
&& !codeAnalyzer.getMethod().isStatic() |
|
|
|
|
&& (((LocalLoadOperator) operands[0]).getLocalInfo() |
|
|
|
|
.equals(codeAnalyzer.getParamInfo(0)))); |
|
|
|
|
boolean opIsThis = !staticFlag && operands[0] instanceof ThisOperator; |
|
|
|
|
int arg = 1; |
|
|
|
|
|
|
|
|
|
if (isConstructor()) { |
|
|
|
|
ClassInfo outer = getOuterClass(); |
|
|
|
|
if (outer != null) { |
|
|
|
|
operands[arg++].dumpExpression(writer, 0); |
|
|
|
|
writer.print("."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (specialFlag) { |
|
|
|
|
if (opIsThis) { |
|
|
|
|
if (opIsThis |
|
|
|
|
&& (((ThisOperator)operands[0]).getClassInfo() |
|
|
|
|
== codeAnalyzer.getClazz())) { |
|
|
|
|
if (isThis()) { |
|
|
|
|
/* XXX check if this is a private or final method. */ |
|
|
|
|
} else { |
|
|
|
@ -159,29 +229,42 @@ public final class InvokeOperator extends Operator |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
/* XXX check if this is a private or final method. */ |
|
|
|
|
int minPriority = 950; /* field access */ |
|
|
|
|
if (!isThis()) { |
|
|
|
|
writer.print("(NON VIRTUAL "); |
|
|
|
|
writer.printType(clazz); |
|
|
|
|
writer.printType(classType); |
|
|
|
|
writer.print(")"); |
|
|
|
|
minPriority = 700; |
|
|
|
|
} |
|
|
|
|
operands[0].dumpExpression(writer, 950); |
|
|
|
|
operands[0].dumpExpression(writer, minPriority); |
|
|
|
|
} |
|
|
|
|
} else if (staticFlag) { |
|
|
|
|
arg = 0; |
|
|
|
|
if (isThis()) |
|
|
|
|
Scope scope = writer.getScope(getClassInfo(), |
|
|
|
|
Scope.CLASSSCOPE); |
|
|
|
|
if (scope != null |
|
|
|
|
&& !writer.conflicts(methodName, scope, Scope.METHODNAME)) |
|
|
|
|
opIsThis = true; |
|
|
|
|
else |
|
|
|
|
writer.printType(clazz); |
|
|
|
|
writer.printType(classType); |
|
|
|
|
} else { |
|
|
|
|
if (!opIsThis) { |
|
|
|
|
int minPriority = 950; /* field access */ |
|
|
|
|
if (opIsThis) { |
|
|
|
|
ThisOperator thisOp = (ThisOperator) operands[0]; |
|
|
|
|
Scope scope = writer.getScope(thisOp.getClassInfo(), |
|
|
|
|
Scope.CLASSSCOPE); |
|
|
|
|
if (writer.conflicts(methodName, scope, Scope.METHODNAME)) { |
|
|
|
|
thisOp.dumpExpression(writer, 950); |
|
|
|
|
writer.print("."); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if (operands[0].getType() instanceof NullType) { |
|
|
|
|
writer.print("("); |
|
|
|
|
writer.printType(clazz); |
|
|
|
|
writer.print("(("); |
|
|
|
|
writer.printType(classType); |
|
|
|
|
writer.print(") "); |
|
|
|
|
minPriority = 700; |
|
|
|
|
} |
|
|
|
|
operands[0].dumpExpression(writer, minPriority); |
|
|
|
|
operands[0].dumpExpression(writer, 700); |
|
|
|
|
writer.print(")"); |
|
|
|
|
} else |
|
|
|
|
operands[0].dumpExpression(writer, 950); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -194,9 +277,12 @@ public final class InvokeOperator extends Operator |
|
|
|
|
writer.print(methodName); |
|
|
|
|
} |
|
|
|
|
writer.print("("); |
|
|
|
|
for (int i=0; i < methodType.getParameterTypes().length; i++) { |
|
|
|
|
if (i>0) |
|
|
|
|
boolean first = true; |
|
|
|
|
while (arg < operands.length) { |
|
|
|
|
if (!first) |
|
|
|
|
writer.print(", "); |
|
|
|
|
else |
|
|
|
|
first = false; |
|
|
|
|
operands[arg++].dumpExpression(writer, 0); |
|
|
|
|
} |
|
|
|
|
writer.print(")"); |
|
|
|
@ -207,9 +293,12 @@ public final class InvokeOperator extends Operator |
|
|
|
|
* @return true if this is the magic class$ method, false otherwise. |
|
|
|
|
*/ |
|
|
|
|
public boolean isGetClass() { |
|
|
|
|
return isThis() |
|
|
|
|
&& codeAnalyzer.getClassAnalyzer() |
|
|
|
|
.getMethod(methodName, methodType).isGetClass(); |
|
|
|
|
if (isThis()) { |
|
|
|
|
SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic(); |
|
|
|
|
if (synth != null && synth.getKind() == SyntheticAnalyzer.GETCLASS) |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class Environment extends SimpleRuntimeEnvironment { |
|
|
|
@ -272,6 +361,56 @@ public final class InvokeOperator extends Operator |
|
|
|
|
return new ConstOperator(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Expression simplifyAccess(Expression[] subs) { |
|
|
|
|
if (isOuter()) { |
|
|
|
|
SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic(); |
|
|
|
|
if (synth != null) { |
|
|
|
|
Operator op = null; |
|
|
|
|
switch (synth.getKind()) { |
|
|
|
|
case SyntheticAnalyzer.ACCESSGETFIELD: |
|
|
|
|
op = new GetFieldOperator(codeAnalyzer, false, |
|
|
|
|
synth.getReference()); |
|
|
|
|
break; |
|
|
|
|
case SyntheticAnalyzer.ACCESSGETSTATIC: |
|
|
|
|
op = new GetFieldOperator(codeAnalyzer, true, |
|
|
|
|
synth.getReference()); |
|
|
|
|
break; |
|
|
|
|
case SyntheticAnalyzer.ACCESSPUTFIELD: |
|
|
|
|
op = new PutFieldOperator(codeAnalyzer, false, |
|
|
|
|
synth.getReference()); |
|
|
|
|
break; |
|
|
|
|
case SyntheticAnalyzer.ACCESSPUTSTATIC: |
|
|
|
|
op = new PutFieldOperator(codeAnalyzer, true, |
|
|
|
|
synth.getReference()); |
|
|
|
|
break; |
|
|
|
|
case SyntheticAnalyzer.ACCESSMETHOD: |
|
|
|
|
op = new InvokeOperator(codeAnalyzer, false, |
|
|
|
|
false, synth.getReference()); |
|
|
|
|
break; |
|
|
|
|
case SyntheticAnalyzer.ACCESSSTATICMETHOD: |
|
|
|
|
op = new InvokeOperator(codeAnalyzer, true, |
|
|
|
|
false, synth.getReference()); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (op != null) { |
|
|
|
|
if (subs == null) |
|
|
|
|
return op; |
|
|
|
|
return new ComplexExpression(op, subs); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Expression simplify() { |
|
|
|
|
Expression expr = simplifyAccess(null); |
|
|
|
|
if (expr != null) |
|
|
|
|
return expr.simplify(); |
|
|
|
|
return super.simplify(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Invokes never equals: they may return different values even if |
|
|
|
|
* they have the same parameters. |
|
|
|
|
*/ |
|
|
|
|