IDEA-285172 - [decompiler] - StrongConnectivityHelper refactoring
[idea/community.git] / plugins / java-decompiler / engine / src / org / jetbrains / java / decompiler / modules / decompiler / IdeaNotNullHelper.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.java.decompiler.modules.decompiler;
3
4 import org.jetbrains.java.decompiler.code.CodeConstants;
5 import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
6 import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
7 import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
8 import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
9 import org.jetbrains.java.decompiler.struct.StructMethod;
10 import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute;
11 import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute;
12 import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
13 import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
14
15 import java.util.List;
16
17 public final class IdeaNotNullHelper {
18
19
20   public static boolean removeHardcodedChecks(Statement root, StructMethod mt) {
21
22     boolean checks_removed = false;
23
24     // parameter @NotNull annotations
25     while (findAndRemoveParameterCheck(root, mt)) { // iterate until nothing found. Each invocation removes one parameter check.
26       checks_removed = true;
27     }
28
29     // method @NotNull annotation
30     while (findAndRemoveReturnCheck(root, mt)) { // iterate until nothing found. Each invocation handles one method exit check.
31       checks_removed = true;
32     }
33
34     return checks_removed;
35   }
36
37   private static boolean findAndRemoveParameterCheck(Statement stat, StructMethod mt) {
38
39     Statement st = stat.getFirst();
40     while (st.type == Statement.TYPE_SEQUENCE) {
41       st = st.getFirst();
42     }
43
44     if (st.type == Statement.TYPE_IF) {
45
46       IfStatement ifstat = (IfStatement)st;
47       Statement ifbranch = ifstat.getIfstat();
48
49       Exprent if_condition = ifstat.getHeadexprent().getCondition();
50
51       boolean is_notnull_check = false;
52
53       // TODO: FUNCTION_NE also possible if reversed order (in theory)
54       if (ifbranch != null &&
55           if_condition.type == Exprent.EXPRENT_FUNCTION &&
56           ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ &&
57           ifbranch.type == Statement.TYPE_BASIC_BLOCK &&
58           ifbranch.getExprents().size() == 1 &&
59           ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {
60
61         FunctionExprent func = (FunctionExprent)if_condition;
62         Exprent first_param = func.getLstOperands().get(0);
63         Exprent second_param = func.getLstOperands().get(1);
64
65         if (second_param.type == Exprent.EXPRENT_CONST &&
66             second_param.getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order
67           if (first_param.type == Exprent.EXPRENT_VAR) {
68             VarExprent var = (VarExprent)first_param;
69
70             boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
71
72             MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
73
74             // parameter annotations
75             StructAnnotationParameterAttribute param_annotations =
76               mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
77             if (param_annotations != null) {
78
79               List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations();
80               int method_param_number = md.params.length;
81
82               int index = thisvar ? 1 : 0;
83               for (int i = 0; i < method_param_number; i++) {
84
85                 if (index == var.getIndex()) {
86                   if (param_annotations_lists.size() >= method_param_number - i) {
87                     int shift = method_param_number -
88                                 param_annotations_lists
89                                   .size(); // NOTE: workaround for compiler bug, count annotations starting with the last parameter
90
91                     List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift);
92
93                     for (AnnotationExprent ann : annotations) {
94                       if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) {
95                         is_notnull_check = true;
96                         break;
97                       }
98                     }
99                   }
100
101                   break;
102                 }
103
104                 index += md.params[i].stackSize;
105               }
106             }
107           }
108         }
109       }
110
111       if (!is_notnull_check) {
112         return false;
113       }
114
115       removeParameterCheck(stat);
116
117       return true;
118     }
119
120     return false;
121   }
122
123   private static void removeParameterCheck(Statement stat) {
124
125     Statement st = stat.getFirst();
126     while (st.type == Statement.TYPE_SEQUENCE) {
127       st = st.getFirst();
128     }
129
130     IfStatement ifstat = (IfStatement)st;
131
132     if (ifstat.getElsestat() != null) { // if - else
133       StatEdge ifedge = ifstat.getIfEdge();
134       StatEdge elseedge = ifstat.getElseEdge();
135
136       Statement ifbranch = ifstat.getIfstat();
137       Statement elsebranch = ifstat.getElsestat();
138
139       ifstat.getFirst().removeSuccessor(ifedge);
140       ifstat.getFirst().removeSuccessor(elseedge);
141
142       ifstat.getStats().removeWithKey(ifbranch.id);
143       ifstat.getStats().removeWithKey(elsebranch.id);
144
145       if (!ifbranch.getAllSuccessorEdges().isEmpty()) {
146         ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0));
147       }
148
149       ifstat.getParent().replaceStatement(ifstat, elsebranch);
150       ifstat.getParent().setAllParent();
151     }
152   }
153
154   private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) {
155
156     boolean is_notnull_check = false;
157
158     // method annotation, refers to the return value
159     StructAnnotationAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS);
160     if (attr != null) {
161       List<AnnotationExprent> annotations = attr.getAnnotations();
162
163       for (AnnotationExprent ann : annotations) {
164         if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) {
165           is_notnull_check = true;
166           break;
167         }
168       }
169     }
170
171     return is_notnull_check && removeReturnCheck(stat, mt);
172   }
173
174
175   private static boolean removeReturnCheck(Statement stat, StructMethod mt) {
176
177     Statement parent = stat.getParent();
178
179     if (parent != null && parent.type == Statement.TYPE_IF && stat.type == Statement.TYPE_BASIC_BLOCK && stat.getExprents().size() == 1) {
180       Exprent exprent = stat.getExprents().get(0);
181       if (exprent.type == Exprent.EXPRENT_EXIT) {
182         ExitExprent exit_exprent = (ExitExprent)exprent;
183         if (exit_exprent.getExitType() == ExitExprent.EXIT_RETURN) {
184           Exprent exprent_value = exit_exprent.getValue();
185           //if(exprent_value.type == Exprent.EXPRENT_VAR) {
186           //    VarExprent var_value = (VarExprent)exprent_value;
187
188           IfStatement ifparent = (IfStatement)parent;
189           Exprent if_condition = ifparent.getHeadexprent().getCondition();
190
191           if (ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION &&
192               ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory)
193
194             FunctionExprent func = (FunctionExprent)if_condition;
195             Exprent first_param = func.getLstOperands().get(0);
196             Exprent second_param = func.getLstOperands().get(1);
197
198             StatEdge ifedge = ifparent.getIfEdge();
199             StatEdge elseedge = ifparent.getElseEdge();
200
201             Statement ifbranch = ifparent.getIfstat();
202             Statement elsebranch = ifparent.getElsestat();
203
204             if (second_param.type == Exprent.EXPRENT_CONST &&
205                 second_param.getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order
206               //if(first_param.type == Exprent.EXPRENT_VAR && ((VarExprent)first_param).getIndex() == var_value.getIndex()) {
207               if (first_param.equals(exprent_value)) {        // TODO: check for absence of side effects like method invocations etc.
208                 if (ifbranch.type == Statement.TYPE_BASIC_BLOCK &&
209                     ifbranch.getExprents().size() == 1 &&
210                     // TODO: special check for IllegalStateException
211                     ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {
212
213                   ifparent.getFirst().removeSuccessor(ifedge);
214                   ifparent.getFirst().removeSuccessor(elseedge);
215
216                   ifparent.getStats().removeWithKey(ifbranch.id);
217                   ifparent.getStats().removeWithKey(elsebranch.id);
218
219                   if (!ifbranch.getAllSuccessorEdges().isEmpty()) {
220                     ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0));
221                   }
222
223                   if (!ifparent.getFirst().getExprents().isEmpty()) {
224                     elsebranch.getExprents().addAll(0, ifparent.getFirst().getExprents());
225                   }
226
227                   ifparent.getParent().replaceStatement(ifparent, elsebranch);
228                   ifparent.getParent().setAllParent();
229
230                   return true;
231                 }
232               }
233             }
234           }
235           //}
236         }
237       }
238     }
239     else if (parent != null &&
240              parent.type == Statement.TYPE_SEQUENCE &&
241              stat.type == Statement.TYPE_BASIC_BLOCK &&
242              stat.getExprents().size() == 1) {
243       Exprent exprent = stat.getExprents().get(0);
244       if (exprent.type == Exprent.EXPRENT_EXIT) {
245         ExitExprent exit_exprent = (ExitExprent)exprent;
246         if (exit_exprent.getExitType() == ExitExprent.EXIT_RETURN) {
247           Exprent exprent_value = exit_exprent.getValue();
248
249           SequenceStatement sequence = (SequenceStatement)parent;
250           int sequence_stats_number = sequence.getStats().size();
251
252           if (sequence_stats_number > 1 &&
253               sequence.getStats().getLast() == stat &&
254               sequence.getStats().get(sequence_stats_number - 2).type == Statement.TYPE_IF) {
255
256             IfStatement ifstat = (IfStatement)sequence.getStats().get(sequence_stats_number - 2);
257             Exprent if_condition = ifstat.getHeadexprent().getCondition();
258
259             if (ifstat.iftype == IfStatement.IFTYPE_IF && if_condition.type == Exprent.EXPRENT_FUNCTION &&
260                 ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory)
261
262               FunctionExprent func = (FunctionExprent)if_condition;
263               Exprent first_param = func.getLstOperands().get(0);
264               Exprent second_param = func.getLstOperands().get(1);
265
266               Statement ifbranch = ifstat.getIfstat();
267
268               if (second_param.type == Exprent.EXPRENT_CONST &&
269                   second_param.getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order
270                 if (first_param.equals(exprent_value)) {        // TODO: check for absence of side effects like method invocations etc.
271                   if (ifbranch.type == Statement.TYPE_BASIC_BLOCK &&
272                       ifbranch.getExprents().size() == 1 &&
273                       // TODO: special check for IllegalStateException
274                       ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {
275
276                     ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); // remove 'else' edge
277
278                     if (!ifstat.getFirst().getExprents().isEmpty()) {
279                       stat.getExprents().addAll(0, ifstat.getFirst().getExprents());
280                     }
281
282                     for (StatEdge edge : ifstat.getAllPredecessorEdges()) {
283
284                       ifstat.removePredecessor(edge);
285                       edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat);
286                       stat.addPredecessor(edge);
287                     }
288
289                     sequence.getStats().removeWithKey(ifstat.id);
290                     sequence.setFirst(sequence.getStats().get(0));
291
292                     return true;
293                   }
294                 }
295               }
296             }
297           }
298         }
299       }
300     }
301
302
303     for (Statement st : stat.getStats()) {
304       if (removeReturnCheck(st, mt)) {
305         return true;
306       }
307     }
308
309     return false;
310   }
311 }