IDEA-151950 Decompiler doesn't work for classes from JDK 9 - support java 9 string...
[idea/community.git] / plugins / java-decompiler / engine / src / org / jetbrains / java / decompiler / modules / decompiler / exps / InvocationExprent.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.java.decompiler.modules.decompiler.exps;
17
18 import org.jetbrains.java.decompiler.code.CodeConstants;
19 import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
20 import org.jetbrains.java.decompiler.main.DecompilerContext;
21 import org.jetbrains.java.decompiler.main.TextBuffer;
22 import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
23 import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
24 import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
25 import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
26 import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
27 import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
28 import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
29 import org.jetbrains.java.decompiler.struct.StructClass;
30 import org.jetbrains.java.decompiler.struct.StructMethod;
31 import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
32 import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
33 import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
34 import org.jetbrains.java.decompiler.struct.gen.VarType;
35 import org.jetbrains.java.decompiler.struct.match.MatchEngine;
36 import org.jetbrains.java.decompiler.struct.match.MatchNode;
37 import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
38 import org.jetbrains.java.decompiler.util.InterpreterUtil;
39 import org.jetbrains.java.decompiler.util.ListStack;
40 import org.jetbrains.java.decompiler.util.TextUtil;
41
42 import java.util.*;
43 import java.util.Map.Entry;
44
45 public class InvocationExprent extends Exprent {
46
47   public static final int INVOKE_SPECIAL = 1;
48   public static final int INVOKE_VIRTUAL = 2;
49   public static final int INVOKE_STATIC = 3;
50   public static final int INVOKE_INTERFACE = 4;
51   public static final int INVOKE_DYNAMIC = 5;
52
53   public static final int TYP_GENERAL = 1;
54   public static final int TYP_INIT = 2;
55   public static final int TYP_CLINIT = 3;
56
57   private static final BitSet EMPTY_BIT_SET = new BitSet(0);
58
59   private String name;
60   private String classname;
61   private boolean isStatic;
62   private int functype = TYP_GENERAL;
63   private Exprent instance;
64   private MethodDescriptor descriptor;
65   private String stringDescriptor;
66   private String invokeDynamicClassSuffix;
67   private int invocationTyp = INVOKE_VIRTUAL;
68   private List<Exprent> lstParameters = new ArrayList<Exprent>();
69   private List<PooledConstant> bootstrapArguments;
70
71   public InvocationExprent() {
72     super(EXPRENT_INVOCATION);
73   }
74
75   public InvocationExprent(int opcode,
76                            LinkConstant cn,
77                            List<PooledConstant> bootstrapArguments,
78                            ListStack<Exprent> stack,
79                            Set<Integer> bytecodeOffsets) {
80     this();
81
82     name = cn.elementname;
83     classname = cn.classname;
84     this.bootstrapArguments = bootstrapArguments;
85
86     switch (opcode) {
87       case CodeConstants.opc_invokestatic:
88         invocationTyp = INVOKE_STATIC;
89         break;
90       case CodeConstants.opc_invokespecial:
91         invocationTyp = INVOKE_SPECIAL;
92         break;
93       case CodeConstants.opc_invokevirtual:
94         invocationTyp = INVOKE_VIRTUAL;
95         break;
96       case CodeConstants.opc_invokeinterface:
97         invocationTyp = INVOKE_INTERFACE;
98         break;
99       case CodeConstants.opc_invokedynamic:
100         invocationTyp = INVOKE_DYNAMIC;
101
102         classname = "java/lang/Class"; // dummy class name
103         invokeDynamicClassSuffix = "##Lambda_" + cn.index1 + "_" + cn.index2;
104     }
105
106     if (CodeConstants.INIT_NAME.equals(name)) {
107       functype = TYP_INIT;
108     }
109     else if (CodeConstants.CLINIT_NAME.equals(name)) {
110       functype = TYP_CLINIT;
111     }
112
113     stringDescriptor = cn.descriptor;
114     descriptor = MethodDescriptor.parseDescriptor(cn.descriptor);
115
116     for (VarType ignored : descriptor.params) {
117       lstParameters.add(0, stack.pop());
118     }
119
120     if (opcode == CodeConstants.opc_invokedynamic) {
121       int dynamicInvocationType = -1;
122       if (bootstrapArguments != null) {
123         if (bootstrapArguments.size() > 1) { // INVOKEDYNAMIC is used not only for lambdas
124           PooledConstant link = bootstrapArguments.get(1);
125           if (link instanceof LinkConstant) {
126             dynamicInvocationType = ((LinkConstant)link).index1;
127           }
128         }
129       }
130       if (dynamicInvocationType == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) {
131         isStatic = true;
132       }
133       else {
134         // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method.
135         if (!lstParameters.isEmpty()) {
136           instance = lstParameters.get(0);
137         }
138       }
139     }
140     else if (opcode == CodeConstants.opc_invokestatic) {
141       isStatic = true;
142     }
143     else {
144       instance = stack.pop();
145     }
146
147     addBytecodeOffsets(bytecodeOffsets);
148   }
149
150   private InvocationExprent(InvocationExprent expr) {
151     this();
152
153     name = expr.getName();
154     classname = expr.getClassname();
155     isStatic = expr.isStatic();
156     functype = expr.getFunctype();
157     instance = expr.getInstance();
158     if (instance != null) {
159       instance = instance.copy();
160     }
161     invocationTyp = expr.getInvocationTyp();
162     invokeDynamicClassSuffix = expr.getInvokeDynamicClassSuffix();
163     stringDescriptor = expr.getStringDescriptor();
164     descriptor = expr.getDescriptor();
165     lstParameters = new ArrayList<Exprent>(expr.getLstParameters());
166     ExprProcessor.copyEntries(lstParameters);
167
168     addBytecodeOffsets(expr.bytecode);
169     bootstrapArguments = expr.getBootstrapArguments();
170   }
171
172   @Override
173   public VarType getExprType() {
174     return descriptor.ret;
175   }
176
177   @Override
178   public CheckTypesResult checkExprTypeBounds() {
179     CheckTypesResult result = new CheckTypesResult();
180
181     for (int i = 0; i < lstParameters.size(); i++) {
182       Exprent parameter = lstParameters.get(i);
183
184       VarType leftType = descriptor.params[i];
185
186       result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.typeFamily));
187       result.addMaxTypeExprent(parameter, leftType);
188     }
189
190     return result;
191   }
192
193   @Override
194   public List<Exprent> getAllExprents() {
195     List<Exprent> lst = new ArrayList<Exprent>();
196     if (instance != null) {
197       lst.add(instance);
198     }
199     lst.addAll(lstParameters);
200     return lst;
201   }
202
203
204   @Override
205   public Exprent copy() {
206     return new InvocationExprent(this);
207   }
208
209   @Override
210   public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
211     TextBuffer buf = new TextBuffer();
212
213     String super_qualifier = null;
214     boolean isInstanceThis = false;
215
216     tracer.addMapping(bytecode);
217
218     if (isStatic) {
219       ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
220       if (node == null || !classname.equals(node.classStruct.qualifiedName)) {
221         buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
222       }
223     }
224     else {
225
226       if (instance != null && instance.type == Exprent.EXPRENT_VAR) {
227         VarExprent instvar = (VarExprent)instance;
228         VarVersionPair varpaar = new VarVersionPair(instvar);
229
230         VarProcessor vproc = instvar.getProcessor();
231         if (vproc == null) {
232           MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
233           if (current_meth != null) {
234             vproc = current_meth.varproc;
235           }
236         }
237
238         String this_classname = null;
239         if (vproc != null) {
240           this_classname = vproc.getThisVars().get(varpaar);
241         }
242
243         if (this_classname != null) {
244           isInstanceThis = true;
245
246           if (invocationTyp == INVOKE_SPECIAL) {
247             if (!classname.equals(this_classname)) { // TODO: direct comparison to the super class?
248               super_qualifier = this_classname;
249             }
250           }
251         }
252       }
253
254       if (functype == TYP_GENERAL) {
255         if (super_qualifier != null) {
256           TextUtil.writeQualifiedSuper(buf, super_qualifier);
257         }
258         else if (instance != null) {
259           TextBuffer res = instance.toJava(indent, tracer);
260
261           VarType rightType = instance.getExprType();
262           VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, classname);
263
264           if (rightType.equals(VarType.VARTYPE_OBJECT) && !leftType.equals(rightType)) {
265             buf.append("((").append(ExprProcessor.getCastTypeName(leftType)).append(")");
266
267             if (instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {
268               res.enclose("(", ")");
269             }
270             buf.append(res).append(")");
271           }
272           else if (instance.getPrecedence() > getPrecedence()) {
273             buf.append("(").append(res).append(")");
274           }
275           else {
276             buf.append(res);
277           }
278         }
279       }
280     }
281
282     switch (functype) {
283       case TYP_GENERAL:
284         if (VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) {
285           buf = new TextBuffer();
286         }
287
288         if (buf.length() > 0) {
289           buf.append(".");
290         }
291
292         buf.append(name);
293         if (invocationTyp == INVOKE_DYNAMIC) {
294           buf.append("<invokedynamic>");
295         }
296         buf.append("(");
297
298         break;
299       case TYP_CLINIT:
300         throw new RuntimeException("Explicit invocation of " + CodeConstants.CLINIT_NAME);
301       case TYP_INIT:
302         if (super_qualifier != null) {
303           buf.append("super(");
304         }
305         else if (isInstanceThis) {
306           buf.append("this(");
307         }
308         else {
309           throw new RuntimeException("Unrecognized invocation of " + CodeConstants.INIT_NAME);
310         }
311     }
312
313     List<VarVersionPair> sigFields = null;
314     boolean isEnum = false;
315     if (functype == TYP_INIT) {
316       ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);
317
318       if (newNode != null) {  // own class
319         if (newNode.getWrapper() != null) {
320           sigFields = newNode.getWrapper().getMethodWrapper(CodeConstants.INIT_NAME, stringDescriptor).signatureFields;
321         }
322         else {
323           if (newNode.type == ClassNode.CLASS_MEMBER && (newNode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class
324             sigFields = new ArrayList<VarVersionPair>(Collections.nCopies(lstParameters.size(), (VarVersionPair)null));
325             sigFields.set(0, new VarVersionPair(-1, 0));
326           }
327         }
328         isEnum = newNode.classStruct.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
329       }
330     }
331
332     BitSet setAmbiguousParameters = getAmbiguousParameters();
333
334     boolean firstParameter = true;
335     int start = isEnum ? 2 : 0;
336     for (int i = start; i < lstParameters.size(); i++) {
337       if (sigFields == null) {
338         if (!firstParameter) {
339           buf.append(", ");
340         }
341
342         TextBuffer buff = new TextBuffer();
343         boolean ambiguous = setAmbiguousParameters.get(i);
344         ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, ambiguous, tracer);
345         buf.append(buff);
346
347         firstParameter = false;
348       }
349     }
350
351     buf.append(")");
352
353     return buf;
354   }
355
356   private BitSet getAmbiguousParameters() {
357     StructClass cl = DecompilerContext.getStructContext().getClass(classname);
358     if (cl == null) return EMPTY_BIT_SET;
359
360     // check number of matches
361     List<MethodDescriptor> matches = new ArrayList<MethodDescriptor>();
362     nextMethod:
363     for (StructMethod mt : cl.getMethods()) {
364       if (name.equals(mt.getName())) {
365         MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
366         if (md.params.length == descriptor.params.length) {
367           for (int i = 0; i < md.params.length; i++) {
368             if (md.params[i].typeFamily != descriptor.params[i].typeFamily) {
369               continue nextMethod;
370             }
371           }
372           matches.add(md);
373         }
374       }
375     }
376     if (matches.size() == 1) return EMPTY_BIT_SET;
377
378     // check if a call is unambiguous
379     StructMethod mt = cl.getMethod(InterpreterUtil.makeUniqueKey(name, stringDescriptor));
380     if (mt != null) {
381       MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
382       if (md.params.length == lstParameters.size()) {
383         boolean exact = true;
384         for (int i = 0; i < md.params.length; i++) {
385           if (!md.params[i].equals(lstParameters.get(i).getExprType())) {
386             exact = false;
387             break;
388           }
389         }
390         if (exact) return EMPTY_BIT_SET;
391       }
392     }
393
394     // mark parameters
395     BitSet ambiguous = new BitSet(descriptor.params.length);
396     for (int i = 0; i < descriptor.params.length; i++) {
397       VarType paramType = descriptor.params[i];
398       for (MethodDescriptor md : matches) {
399         if (!paramType.equals(md.params[i])) {
400           ambiguous.set(i);
401           break;
402         }
403       }
404     }
405     return ambiguous;
406   }
407
408   @Override
409   public void replaceExprent(Exprent oldExpr, Exprent newExpr) {
410     if (oldExpr == instance) {
411       instance = newExpr;
412     }
413
414     for (int i = 0; i < lstParameters.size(); i++) {
415       if (oldExpr == lstParameters.get(i)) {
416         lstParameters.set(i, newExpr);
417       }
418     }
419   }
420
421   @Override
422   public boolean equals(Object o) {
423     if (o == this) return true;
424     if (o == null || !(o instanceof InvocationExprent)) return false;
425
426     InvocationExprent it = (InvocationExprent)o;
427     return InterpreterUtil.equalObjects(name, it.getName()) &&
428            InterpreterUtil.equalObjects(classname, it.getClassname()) &&
429            isStatic == it.isStatic() &&
430            InterpreterUtil.equalObjects(instance, it.getInstance()) &&
431            InterpreterUtil.equalObjects(descriptor, it.getDescriptor()) &&
432            functype == it.getFunctype() &&
433            InterpreterUtil.equalLists(lstParameters, it.getLstParameters());
434   }
435
436   public List<Exprent> getLstParameters() {
437     return lstParameters;
438   }
439
440   public void setLstParameters(List<Exprent> lstParameters) {
441     this.lstParameters = lstParameters;
442   }
443
444   public MethodDescriptor getDescriptor() {
445     return descriptor;
446   }
447
448   public void setDescriptor(MethodDescriptor descriptor) {
449     this.descriptor = descriptor;
450   }
451
452   public String getClassname() {
453     return classname;
454   }
455
456   public void setClassname(String classname) {
457     this.classname = classname;
458   }
459
460   public int getFunctype() {
461     return functype;
462   }
463
464   public void setFunctype(int functype) {
465     this.functype = functype;
466   }
467
468   public Exprent getInstance() {
469     return instance;
470   }
471
472   public void setInstance(Exprent instance) {
473     this.instance = instance;
474   }
475
476   public boolean isStatic() {
477     return isStatic;
478   }
479
480   public void setStatic(boolean isStatic) {
481     this.isStatic = isStatic;
482   }
483
484   public String getName() {
485     return name;
486   }
487
488   public void setName(String name) {
489     this.name = name;
490   }
491
492   public String getStringDescriptor() {
493     return stringDescriptor;
494   }
495
496   public void setStringDescriptor(String stringDescriptor) {
497     this.stringDescriptor = stringDescriptor;
498   }
499
500   public int getInvocationTyp() {
501     return invocationTyp;
502   }
503
504   public String getInvokeDynamicClassSuffix() {
505     return invokeDynamicClassSuffix;
506   }
507
508   public List<PooledConstant> getBootstrapArguments() {
509     return bootstrapArguments;
510   }
511
512   // *****************************************************************************
513   // IMatchable implementation
514   // *****************************************************************************
515   
516   public boolean match(MatchNode matchNode, MatchEngine engine) {
517
518     if(!super.match(matchNode, engine)) {
519       return false;
520     }
521     
522     for(Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {
523       RuleValue value = rule.getValue();
524       
525       switch(rule.getKey()) {
526       case EXPRENT_INVOCATION_PARAMETER:
527         if(value.isVariable()) {
528           if(value.parameter < lstParameters.size()) {
529             if(!engine.checkAndSetVariableValue(value.value.toString(), lstParameters.get(value.parameter))) {
530               return false;
531             }
532           } else {
533             return false;
534           }
535         }
536         break;
537       case EXPRENT_INVOCATION_CLASS:
538         if(!value.value.equals(this.classname)) {
539           return false;
540         }
541         break;
542       case EXPRENT_INVOCATION_SIGNATURE:
543         if(!value.value.equals(this.name + this.stringDescriptor)) {
544           return false;
545         }
546         break;
547       }
548       
549     }
550     
551     return true;
552   }
553
554 }