Fork of the Fernflower decompiler
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.
fernflower/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java

499 lines
13 KiB

/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
*
* (C) 2008 - 2010, Stiver
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
*/
package de.fernflower.modules.decompiler.exps;
import java.util.*;
import de.fernflower.code.CodeConstants;
import de.fernflower.main.ClassesProcessor.ClassNode;
import de.fernflower.main.DecompilerContext;
import de.fernflower.main.extern.IFernflowerPreferences;
import de.fernflower.main.rels.MethodWrapper;
import de.fernflower.modules.decompiler.ExprProcessor;
import de.fernflower.modules.decompiler.vars.CheckTypesResult;
import de.fernflower.modules.decompiler.vars.VarProcessor;
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
import de.fernflower.struct.StructClass;
import de.fernflower.struct.StructMethod;
import de.fernflower.struct.consts.LinkConstant;
import de.fernflower.struct.gen.MethodDescriptor;
import de.fernflower.struct.gen.VarType;
import de.fernflower.util.InterpreterUtil;
import de.fernflower.util.ListStack;
public class InvocationExprent extends Exprent {
public static final int INVOKE_SPECIAL = 1;
public static final int INVOKE_VIRTUAL = 2;
public static final int INVOKE_STATIC = 3;
public static final int INVOKE_INTERFACE = 4;
public static final int INVOKE_DYNAMIC = 5;
public static final int TYP_GENERAL = 1;
public static final int TYP_INIT = 2;
public static final int TYP_CLINIT = 3;
public static final int CONSTRUCTOR_NOT = 0;
public static final int CONSTRUCTOR_THIS = 1;
public static final int CONSTRUCTOR_SUPER = 2;
private String name;
private String classname;
private boolean isStatic;
private int functype = TYP_GENERAL;
private Exprent instance;
private MethodDescriptor descriptor;
private String stringDescriptor;
private String invoke_dynamic_classsuffix;
private int invocationTyp = INVOKE_VIRTUAL;
private List<Exprent> lstParameters = new ArrayList<Exprent>();
{
this.type = EXPRENT_INVOCATION;
}
public InvocationExprent() {}
public InvocationExprent(int opcode, LinkConstant cn, ListStack<Exprent> stack) {
name = cn.elementname;
classname = cn.classname;
switch(opcode) {
case CodeConstants.opc_invokestatic:
invocationTyp = INVOKE_STATIC;
break;
case CodeConstants.opc_invokespecial:
invocationTyp = INVOKE_SPECIAL;
break;
case CodeConstants.opc_invokevirtual:
invocationTyp = INVOKE_VIRTUAL;
break;
case CodeConstants.opc_invokeinterface:
invocationTyp = INVOKE_INTERFACE;
break;
case CodeConstants.opc_invokedynamic:
invocationTyp = INVOKE_DYNAMIC;
classname = "java/lang/Class"; // dummy class name
invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2;
}
if("<init>".equals(name)) {
functype = TYP_INIT;
}else if("<clinit>".equals(name)) {
functype = TYP_CLINIT;
}
stringDescriptor = cn.descriptor;
descriptor = MethodDescriptor.parseDescriptor(cn.descriptor);
for(int i=0;i<descriptor.params.length;i++) {
lstParameters.add(0, stack.pop());
}
if(opcode == CodeConstants.opc_invokestatic || opcode == CodeConstants.opc_invokedynamic) {
isStatic = true;
} else {
instance = stack.pop();
}
}
private InvocationExprent(InvocationExprent expr) {
name = expr.getName();
classname = expr.getClassname();
isStatic = expr.isStatic();
functype = expr.getFunctype();
instance = expr.getInstance();
if(instance != null) {
instance = instance.copy();
}
invocationTyp = expr.getInvocationTyp();
stringDescriptor = expr.getStringDescriptor();
descriptor = expr.getDescriptor();
lstParameters = new ArrayList<Exprent>(expr.getLstParameters());
for(int i=0;i<lstParameters.size();i++) {
lstParameters.set(i, lstParameters.get(i).copy());
}
}
public VarType getExprType() {
return descriptor.ret;
}
public CheckTypesResult checkExprTypeBounds() {
CheckTypesResult result = new CheckTypesResult();
for(int i=0;i<lstParameters.size();i++) {
Exprent parameter = lstParameters.get(i);
VarType leftType = descriptor.params[i];
result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.type_family));
result.addMaxTypeExprent(parameter, leftType);
}
return result;
}
public List<Exprent> getAllExprents() {
List<Exprent> lst = new ArrayList<Exprent>();
if(instance != null) {
lst.add(instance);
}
lst.addAll(lstParameters);
return lst;
}
public Exprent copy() {
return new InvocationExprent(this);
}
public String toJava(int indent) {
StringBuilder buf = new StringBuilder("");
String super_qualifier = null;
boolean isInstanceThis = false;
if(invocationTyp == INVOKE_DYNAMIC) {
// ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
//
// if(node != null) {
// ClassNode lambda_node = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName + invoke_dynamic_classsuffix);
// if(lambda_node != null) {
//
// String typename = ExprProcessor.getCastTypeName(lambda_node.anonimousClassType);
//
// StringWriter strwriter = new StringWriter();
// BufferedWriter bufstrwriter = new BufferedWriter(strwriter);
//
// ClassWriter clwriter = new ClassWriter();
//
// try {
// bufstrwriter.write("new " + typename + "() {");
// bufstrwriter.newLine();
//
//
//
// bufstrwriter.flush();
// } catch(IOException ex) {
// throw new RuntimeException(ex);
// }
//
// buf.append(strwriter.toString());
//
// }
// }
} else if(isStatic) {
ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
if(node == null || !classname.equals(node.classStruct.qualifiedName)) {
buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
}
} else {
if(instance != null && instance.type == Exprent.EXPRENT_VAR) {
VarExprent instvar = (VarExprent)instance;
VarVersionPaar varpaar = new VarVersionPaar(instvar);
VarProcessor vproc = instvar.getProcessor();
if(vproc == null) {
MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
if(current_meth != null) {
vproc = current_meth.varproc;
}
}
String this_classname = null;
if(vproc != null) {
this_classname = vproc.getThisvars().get(varpaar);
}
if(this_classname != null) {
isInstanceThis = true;
if(invocationTyp == INVOKE_SPECIAL) {
if(!classname.equals(this_classname)) { // TODO: direct comparison to the super class?
super_qualifier = this_classname;
}
}
}
}
if(functype == TYP_GENERAL){
if(super_qualifier != null) {
StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct;
if(!super_qualifier.equals(current_class.qualifiedName)) {
buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier)));
buf.append(".");
}
buf.append("super");
} else {
String res = instance.toJava(indent);
VarType rightType = instance.getExprType();
VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, classname);
if(rightType.equals(VarType.VARTYPE_OBJECT) && !leftType.equals(rightType)) {
buf.append("(("+ExprProcessor.getCastTypeName(leftType)+")");
if(instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {
res = "("+res+")";
}
buf.append(res+")");
} else if(instance.getPrecedence() > getPrecedence()) {
buf.append("("+res+")");
} else {
buf.append(res);
}
}
}
}
switch(functype) {
case TYP_GENERAL:
if(VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) {
buf = new StringBuilder("");
}
if(buf.length() > 0) {
buf.append(".");
}
buf.append(name);
if(invocationTyp == INVOKE_DYNAMIC) {
buf.append("<invokedynamic>");
}
buf.append("(");
break;
case TYP_CLINIT:
throw new RuntimeException("Explicite invocation of <clinit>");
case TYP_INIT:
if(super_qualifier != null) {
buf.append("super(");
} else if(isInstanceThis) {
buf.append("this(");
} else {
buf.append(instance.toJava(indent));
buf.append(".<init>(");
// throw new RuntimeException("Unrecognized invocation of <init>"); // FIXME: activate
}
}
List<VarVersionPaar> sigFields = null;
boolean isEnum = false;
if(functype == TYP_INIT) {
ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname);
if(newnode != null) { // own class
if(newnode.wrapper != null) {
sigFields = newnode.wrapper.getMethodWrapper("<init>", stringDescriptor).signatureFields;
} else {
if(newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class
sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null));
sigFields.set(0, new VarVersionPaar(-1, 0));
}
}
isEnum = (newnode.classStruct.access_flags & CodeConstants.ACC_ENUM) != 0 &&
DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
}
}
Set<Integer> setAmbiguousParameters = getAmbiguousParameters();
boolean firstpar = true;
int start = isEnum ? 2 : 0;
for(int i=start;i<lstParameters.size();i++) {
if(sigFields == null || sigFields.get(i) == null) {
if(!firstpar) {
buf.append(", ");
}
StringBuilder buff = new StringBuilder();
ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, setAmbiguousParameters.contains(i));
buf.append(buff);
firstpar = false;
}
}
buf.append(")");
return buf.toString();
}
private Set<Integer> getAmbiguousParameters() {
Set<Integer> ret = new HashSet<Integer>();
StructClass cstr = DecompilerContext.getStructcontext().getClass(classname);
if(cstr != null) {
List<MethodDescriptor> lstMethods = new ArrayList<MethodDescriptor>();
for(StructMethod meth : cstr.getMethods()) {
if(name.equals(meth.getName())) {
MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.getDescriptor());
if(md.params.length == descriptor.params.length) {
boolean equals = true;
for(int i=0;i<md.params.length;i++) {
if(md.params[i].type_family != descriptor.params[i].type_family) {
equals = false;
break;
}
}
if(equals) {
lstMethods.add(md);
}
}
}
}
if(lstMethods.size() > 1) {
for(int i=0;i<descriptor.params.length;i++) {
VarType partype = descriptor.params[i];
for(MethodDescriptor md : lstMethods) {
if(!partype.equals(md.params[i])) {
ret.add(i);
break;
}
}
}
}
}
return ret;
}
public boolean equals(Object o) {
if(o!=null && o instanceof InvocationExprent) {
InvocationExprent it = (InvocationExprent)o;
return InterpreterUtil.equalObjects(name, it.getName()) &&
InterpreterUtil.equalObjects(classname, it.getClassname()) &&
isStatic == it.isStatic() &&
InterpreterUtil.equalObjects(instance, it.getInstance()) &&
InterpreterUtil.equalObjects(descriptor, it.getDescriptor()) &&
functype == it.getFunctype() &&
InterpreterUtil.equalLists(lstParameters, it.getLstParameters());
}
return false;
}
public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
if(oldexpr == instance) {
instance = newexpr;
}
for(int i=0;i<lstParameters.size();i++) {
if(oldexpr == lstParameters.get(i)) {
lstParameters.set(i, newexpr);
}
}
}
public List<Exprent> getLstParameters() {
return lstParameters;
}
public void setLstParameters(List<Exprent> lstParameters) {
this.lstParameters = lstParameters;
}
public MethodDescriptor getDescriptor() {
return descriptor;
}
public void setDescriptor(MethodDescriptor descriptor) {
this.descriptor = descriptor;
}
public String getClassname() {
return classname;
}
public void setClassname(String classname) {
this.classname = classname;
}
public int getFunctype() {
return functype;
}
public void setFunctype(int functype) {
this.functype = functype;
}
public Exprent getInstance() {
return instance;
}
public void setInstance(Exprent instance) {
this.instance = instance;
}
public boolean isStatic() {
return isStatic;
}
public void setStatic(boolean isStatic) {
this.isStatic = isStatic;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStringDescriptor() {
return stringDescriptor;
}
public void setStringDescriptor(String stringDescriptor) {
this.stringDescriptor = stringDescriptor;
}
public int getInvocationTyp() {
return invocationTyp;
}
public void setInvocationTyp(int invocationTyp) {
this.invocationTyp = invocationTyp;
}
public String getInvokeDynamicClassSuffix() {
return invoke_dynamic_classsuffix;
}
}