diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java new file mode 100644 index 000000000000..04758dc0492d --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -0,0 +1,515 @@ +/* + * 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_CLASS_NODE); + if (node == null || !classname.equals(node.classStruct.qualifiedName)) { + buf.append(DecompilerContext.getImportCollector().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_CLASS_NODE)).classStruct; + + if (!super_qualifier.equals(current_class.qualifiedName)) { + buf.append(DecompilerContext.getImportCollector().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.hasModifier(CodeConstants.ACC_ENUM) && 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; + } +} |