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;
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;
15 import java.util.List;
17 public final class IdeaNotNullHelper {
20 public static boolean removeHardcodedChecks(Statement root, StructMethod mt) {
22 boolean checks_removed = false;
24 // parameter @NotNull annotations
25 while (findAndRemoveParameterCheck(root, mt)) { // iterate until nothing found. Each invocation removes one parameter check.
26 checks_removed = true;
29 // method @NotNull annotation
30 while (findAndRemoveReturnCheck(root, mt)) { // iterate until nothing found. Each invocation handles one method exit check.
31 checks_removed = true;
34 return checks_removed;
37 private static boolean findAndRemoveParameterCheck(Statement stat, StructMethod mt) {
39 Statement st = stat.getFirst();
40 while (st.type == Statement.TYPE_SEQUENCE) {
44 if (st.type == Statement.TYPE_IF) {
46 IfStatement ifstat = (IfStatement)st;
47 Statement ifbranch = ifstat.getIfstat();
49 Exprent if_condition = ifstat.getHeadexprent().getCondition();
51 boolean is_notnull_check = false;
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) {
61 FunctionExprent func = (FunctionExprent)if_condition;
62 Exprent first_param = func.getLstOperands().get(0);
63 Exprent second_param = func.getLstOperands().get(1);
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;
70 boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
72 MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
74 // parameter annotations
75 StructAnnotationParameterAttribute param_annotations =
76 mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
77 if (param_annotations != null) {
79 List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations();
80 int method_param_number = md.params.length;
82 int index = thisvar ? 1 : 0;
83 for (int i = 0; i < method_param_number; i++) {
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
91 List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift);
93 for (AnnotationExprent ann : annotations) {
94 if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) {
95 is_notnull_check = true;
104 index += md.params[i].stackSize;
111 if (!is_notnull_check) {
115 removeParameterCheck(stat);
123 private static void removeParameterCheck(Statement stat) {
125 Statement st = stat.getFirst();
126 while (st.type == Statement.TYPE_SEQUENCE) {
130 IfStatement ifstat = (IfStatement)st;
132 if (ifstat.getElsestat() != null) { // if - else
133 StatEdge ifedge = ifstat.getIfEdge();
134 StatEdge elseedge = ifstat.getElseEdge();
136 Statement ifbranch = ifstat.getIfstat();
137 Statement elsebranch = ifstat.getElsestat();
139 ifstat.getFirst().removeSuccessor(ifedge);
140 ifstat.getFirst().removeSuccessor(elseedge);
142 ifstat.getStats().removeWithKey(ifbranch.id);
143 ifstat.getStats().removeWithKey(elsebranch.id);
145 if (!ifbranch.getAllSuccessorEdges().isEmpty()) {
146 ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0));
149 ifstat.getParent().replaceStatement(ifstat, elsebranch);
150 ifstat.getParent().setAllParent();
154 private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) {
156 boolean is_notnull_check = false;
158 // method annotation, refers to the return value
159 StructAnnotationAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS);
161 List<AnnotationExprent> annotations = attr.getAnnotations();
163 for (AnnotationExprent ann : annotations) {
164 if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) {
165 is_notnull_check = true;
171 return is_notnull_check && removeReturnCheck(stat, mt);
175 private static boolean removeReturnCheck(Statement stat, StructMethod mt) {
177 Statement parent = stat.getParent();
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;
188 IfStatement ifparent = (IfStatement)parent;
189 Exprent if_condition = ifparent.getHeadexprent().getCondition();
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)
194 FunctionExprent func = (FunctionExprent)if_condition;
195 Exprent first_param = func.getLstOperands().get(0);
196 Exprent second_param = func.getLstOperands().get(1);
198 StatEdge ifedge = ifparent.getIfEdge();
199 StatEdge elseedge = ifparent.getElseEdge();
201 Statement ifbranch = ifparent.getIfstat();
202 Statement elsebranch = ifparent.getElsestat();
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) {
213 ifparent.getFirst().removeSuccessor(ifedge);
214 ifparent.getFirst().removeSuccessor(elseedge);
216 ifparent.getStats().removeWithKey(ifbranch.id);
217 ifparent.getStats().removeWithKey(elsebranch.id);
219 if (!ifbranch.getAllSuccessorEdges().isEmpty()) {
220 ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0));
223 if (!ifparent.getFirst().getExprents().isEmpty()) {
224 elsebranch.getExprents().addAll(0, ifparent.getFirst().getExprents());
227 ifparent.getParent().replaceStatement(ifparent, elsebranch);
228 ifparent.getParent().setAllParent();
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();
249 SequenceStatement sequence = (SequenceStatement)parent;
250 int sequence_stats_number = sequence.getStats().size();
252 if (sequence_stats_number > 1 &&
253 sequence.getStats().getLast() == stat &&
254 sequence.getStats().get(sequence_stats_number - 2).type == Statement.TYPE_IF) {
256 IfStatement ifstat = (IfStatement)sequence.getStats().get(sequence_stats_number - 2);
257 Exprent if_condition = ifstat.getHeadexprent().getCondition();
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)
262 FunctionExprent func = (FunctionExprent)if_condition;
263 Exprent first_param = func.getLstOperands().get(0);
264 Exprent second_param = func.getLstOperands().get(1);
266 Statement ifbranch = ifstat.getIfstat();
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) {
276 ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); // remove 'else' edge
278 if (!ifstat.getFirst().getExprents().isEmpty()) {
279 stat.getExprents().addAll(0, ifstat.getFirst().getExprents());
282 for (StatEdge edge : ifstat.getAllPredecessorEdges()) {
284 ifstat.removePredecessor(edge);
285 edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat);
286 stat.addPredecessor(edge);
289 sequence.getStats().removeWithKey(ifstat.id);
290 sequence.setFirst(sequence.getStats().get(0));
303 for (Statement st : stat.getStats()) {
304 if (removeReturnCheck(st, mt)) {