summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java
diff options
context:
space:
mode:
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.java515
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;
+ }
+}