517 lines
15 KiB
Java
517 lines
15 KiB
Java
/*
|
|
* Copyright 2000-2014 JetBrains s.r.o.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package org.jetbrains.java.decompiler.modules.decompiler.exps;
|
|
|
|
import org.jetbrains.java.decompiler.code.CodeConstants;
|
|
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
|
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
|
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
|
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
|
|
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
|
|
import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
|
|
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
|
|
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
|
|
import org.jetbrains.java.decompiler.struct.StructClass;
|
|
import org.jetbrains.java.decompiler.struct.StructMethod;
|
|
import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
|
|
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
|
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
|
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
|
import org.jetbrains.java.decompiler.util.ListStack;
|
|
|
|
import java.util.*;
|
|
|
|
|
|
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, int dynamic_invokation_type) {
|
|
|
|
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_invokedynamic) {
|
|
if (dynamic_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) {
|
|
isStatic = true;
|
|
}
|
|
else {
|
|
instance = lstParameters
|
|
.get(0); // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method.
|
|
}
|
|
}
|
|
else if (opcode == CodeConstants.opc_invokestatic) {
|
|
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("((").append(ExprProcessor.getCastTypeName(leftType)).append(")");
|
|
|
|
if (instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {
|
|
res = "(" + res + ")";
|
|
}
|
|
buf.append(res).append(")");
|
|
}
|
|
else if (instance.getPrecedence() > getPrecedence()) {
|
|
buf.append("(").append(res).append(")");
|
|
}
|
|
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 == this) return true;
|
|
if (o == null || !(o instanceof InvocationExprent)) return false;
|
|
|
|
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());
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|