blob: 298b51bc4a24672421a9da26f92f55020ec34e35 [file] [log] [blame]
Tor Norbyeec3fb1e2013-05-31 07:45:51 -07001/*
2 * Copyright 2000-2009 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
17/*
18 * Created by IntelliJ IDEA.
19 * User: max
20 * Date: Jan 28, 2002
21 * Time: 10:16:39 PM
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
24 */
25package com.intellij.codeInspection.dataFlow;
26
27import com.intellij.codeInspection.dataFlow.instructions.BranchingInstruction;
28import com.intellij.codeInspection.dataFlow.instructions.EmptyInstruction;
29import com.intellij.codeInspection.dataFlow.instructions.Instruction;
30import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
31import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
32import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
33import com.intellij.openapi.application.ApplicationManager;
34import com.intellij.openapi.diagnostic.Logger;
35import com.intellij.openapi.progress.ProgressManager;
36import com.intellij.openapi.util.Key;
37import com.intellij.openapi.util.Pair;
38import com.intellij.psi.*;
39import com.intellij.psi.util.PsiTreeUtil;
40import com.intellij.psi.util.PsiUtil;
Tor Norbyec7f983b2013-09-05 16:07:26 -070041import com.intellij.util.containers.MultiMap;
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070042import org.jetbrains.annotations.NotNull;
43import org.jetbrains.annotations.Nullable;
44
45import java.util.*;
46
47public class DataFlowRunner {
48 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DataFlowRunner");
49 private static final Key<Integer> TOO_EXPENSIVE_HASH = Key.create("TOO_EXPENSIVE_HASH");
50 public static final long ourTimeLimit = 1000 * 1000 * 1000; //1 sec in nanoseconds
51
52 private Instruction[] myInstructions;
Tor Norbyec7f983b2013-09-05 16:07:26 -070053 private final MultiMap<PsiElement, DfaMemoryState> myNestedClosures = new MultiMap<PsiElement, DfaMemoryState>();
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070054 private DfaVariableValue[] myFields;
55 private final DfaValueFactory myValueFactory = new DfaValueFactory();
56
57 // Maximum allowed attempts to process instruction. Fail as too complex to process if certain instruction
58 // is executed more than this limit times.
59 public static final int MAX_STATES_PER_BRANCH = 300;
60
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070061 protected DataFlowRunner() {
62 }
63
64 public DfaValueFactory getFactory() {
65 return myValueFactory;
66 }
67
Tor Norbyec7f983b2013-09-05 16:07:26 -070068 protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable<DfaMemoryState> initialStates) {
69 }
70
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070071 @Nullable
Tor Norbyec7f983b2013-09-05 16:07:26 -070072 private Collection<DfaMemoryState> createInitialStates(@NotNull PsiElement psiBlock, InstructionVisitor visitor) {
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070073 PsiClass containingClass = PsiTreeUtil.getParentOfType(psiBlock, PsiClass.class);
74 if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) {
75 final PsiElement parent = containingClass.getParent();
76 final PsiCodeBlock block = DfaPsiUtil.getTopmostBlockInSameClass(parent);
77 if ((parent instanceof PsiNewExpression || parent instanceof PsiDeclarationStatement) && block != null) {
Tor Norbyec7f983b2013-09-05 16:07:26 -070078 final RunnerResult result = analyzeMethod(block, visitor);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070079 if (result == RunnerResult.OK) {
Tor Norbyec7f983b2013-09-05 16:07:26 -070080 final Collection<DfaMemoryState> closureStates = myNestedClosures.get(DfaPsiUtil.getTopmostBlockInSameClass(psiBlock));
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070081 if (!closureStates.isEmpty()) {
82 return closureStates;
83 }
84 }
85 return null;
86 }
87 }
88
89 return Arrays.asList(createMemoryState());
90 }
91
92 public final RunnerResult analyzeMethod(@NotNull PsiElement psiBlock, InstructionVisitor visitor) {
Tor Norbyec7f983b2013-09-05 16:07:26 -070093 Collection<DfaMemoryState> initialStates = createInitialStates(psiBlock, visitor);
94 return initialStates == null ? RunnerResult.NOT_APPLICABLE : analyzeMethod(psiBlock, visitor, false, initialStates);
Tor Norbye9a345242013-07-08 10:50:41 -070095 }
96
Tor Norbyec7f983b2013-09-05 16:07:26 -070097 public final RunnerResult analyzeMethod(@NotNull PsiElement psiBlock,
98 InstructionVisitor visitor,
99 boolean ignoreAssertions,
100 @NotNull Collection<DfaMemoryState> initialStates) {
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700101 try {
Tor Norbyec7f983b2013-09-05 16:07:26 -0700102 prepareAnalysis(psiBlock, initialStates);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700103
Tor Norbye9a345242013-07-08 10:50:41 -0700104 final ControlFlow flow = createControlFlowAnalyzer().buildControlFlow(psiBlock, ignoreAssertions);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700105 if (flow == null) return RunnerResult.NOT_APPLICABLE;
106
107 int endOffset = flow.getInstructionCount();
108 myInstructions = flow.getInstructions();
109 myFields = flow.getFields();
Tor Norbyec7f983b2013-09-05 16:07:26 -0700110 myNestedClosures.clear();
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700111
112 if (LOG.isDebugEnabled()) {
113 LOG.debug("Analyzing code block: " + psiBlock.getText());
114 for (int i = 0; i < myInstructions.length; i++) {
115 Instruction instruction = myInstructions[i];
116 LOG.debug(i + ": " + instruction.toString());
117 }
118 }
119
120 Integer tooExpensiveHash = psiBlock.getUserData(TOO_EXPENSIVE_HASH);
121 if (tooExpensiveHash != null && tooExpensiveHash == psiBlock.getText().hashCode()) {
122 LOG.debug("Too complex because hasn't changed since being too complex already");
123 return RunnerResult.TOO_COMPLEX;
124 }
125
126 final ArrayList<DfaInstructionState> queue = new ArrayList<DfaInstructionState>();
127 for (final DfaMemoryState initialState : initialStates) {
128 queue.add(new DfaInstructionState(myInstructions[0], initialState));
129 }
130
Tor Norbyebeca9832013-09-19 12:39:22 -0700131 WorkingTimeMeasurer measurer = new WorkingTimeMeasurer(shouldCheckTimeLimit() ? ourTimeLimit : ourTimeLimit * 42);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700132 int count = 0;
133 while (!queue.isEmpty()) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700134 if (count % 64 == 0 && measurer.isTimeOver()) {
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700135 LOG.debug("Too complex because the analysis took too long");
136 psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode());
137 return RunnerResult.TOO_COMPLEX;
138 }
139 ProgressManager.checkCanceled();
140
141 DfaInstructionState instructionState = queue.remove(0);
142 if (LOG.isDebugEnabled()) {
143 LOG.debug(instructionState.toString());
144 }
Tor Norbyec7f983b2013-09-05 16:07:26 -0700145 //System.out.println(instructionState.toString());
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700146
147 Instruction instruction = instructionState.getInstruction();
148 long distance = instructionState.getDistanceFromStart();
149
150 if (instruction instanceof BranchingInstruction) {
151 if (!instruction.setMemoryStateProcessed(instructionState.getMemoryState().createCopy())) {
152 LOG.debug("Too complex because too many different possible states");
153 return RunnerResult.TOO_COMPLEX; // Too complex :(
154 }
155 }
156
Tor Norbyec7f983b2013-09-05 16:07:26 -0700157 DfaInstructionState[] after = acceptInstruction(visitor, instructionState);
158 for (DfaInstructionState state : after) {
159 Instruction nextInstruction = state.getInstruction();
160 if ((!(nextInstruction instanceof BranchingInstruction) || !nextInstruction.isMemoryStateProcessed(state.getMemoryState())) && instruction.getIndex() < endOffset) {
161 state.setDistanceFromStart(distance + 1);
162 queue.add(state);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700163 }
164 }
165
166 count++;
167 }
168
169 psiBlock.putUserData(TOO_EXPENSIVE_HASH, null);
170 LOG.debug("Analysis ok");
171 return RunnerResult.OK;
172 }
173 catch (ArrayIndexOutOfBoundsException e) {
174 LOG.error(psiBlock.getText(), e); // TODO fix in better times
175 return RunnerResult.ABORTED;
176 }
177 catch (EmptyStackException e) {
178 if (LOG.isDebugEnabled()) {
179 LOG.error(e); // TODO fix in better times
180 }
181 return RunnerResult.ABORTED;
182 }
183 }
184
Tor Norbyebeca9832013-09-19 12:39:22 -0700185 protected boolean shouldCheckTimeLimit() {
186 return !ApplicationManager.getApplication().isUnitTestMode();
187 }
188
Tor Norbyec7f983b2013-09-05 16:07:26 -0700189 private DfaInstructionState[] acceptInstruction(InstructionVisitor visitor, DfaInstructionState instructionState) {
190 Instruction instruction = instructionState.getInstruction();
191 if (instruction instanceof MethodCallInstruction) {
192 PsiCallExpression anchor = ((MethodCallInstruction)instruction).getCallExpression();
193 if (anchor instanceof PsiNewExpression) {
194 PsiAnonymousClass anonymousClass = ((PsiNewExpression)anchor).getAnonymousClass();
195 if (anonymousClass != null) {
196 registerNestedClosures(instructionState, anonymousClass);
197 }
198 }
199 }
200 else if (instruction instanceof EmptyInstruction) {
201 PsiElement anchor = ((EmptyInstruction)instruction).getAnchor();
202 if (anchor instanceof PsiDeclarationStatement) {
203 for (PsiElement element : ((PsiDeclarationStatement)anchor).getDeclaredElements()) {
204 if (element instanceof PsiClass) {
205 registerNestedClosures(instructionState, (PsiClass)element);
206 }
207 }
208 }
209 }
210
211 return instruction.accept(this, instructionState.getMemoryState(), visitor);
212 }
213
214 private void registerNestedClosures(DfaInstructionState instructionState, PsiClass nestedClass) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700215 DfaMemoryState state = instructionState.getMemoryState();
Tor Norbyec7f983b2013-09-05 16:07:26 -0700216 for (PsiMethod method : nestedClass.getMethods()) {
217 PsiCodeBlock body = method.getBody();
218 if (body != null) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700219 myNestedClosures.putValue(body, createClosureState(state));
Tor Norbyec7f983b2013-09-05 16:07:26 -0700220 }
221 }
222 for (PsiClassInitializer initializer : nestedClass.getInitializers()) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700223 myNestedClosures.putValue(initializer.getBody(), createClosureState(state));
Tor Norbyec7f983b2013-09-05 16:07:26 -0700224 }
225 for (PsiField field : nestedClass.getFields()) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700226 myNestedClosures.putValue(field, createClosureState(state));
Tor Norbyec7f983b2013-09-05 16:07:26 -0700227 }
228 }
229
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700230 protected ControlFlowAnalyzer createControlFlowAnalyzer() {
231 return new ControlFlowAnalyzer(myValueFactory);
232 }
233
234 protected DfaMemoryState createMemoryState() {
235 return new DfaMemoryStateImpl(myValueFactory);
236 }
237
238 public Instruction[] getInstructions() {
239 return myInstructions;
240 }
241
Tor Norbyec7f983b2013-09-05 16:07:26 -0700242 public Instruction getInstruction(int index) {
243 return myInstructions[index];
244 }
245
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700246 public DfaVariableValue[] getFields() {
247 return myFields;
248 }
249
Tor Norbyec7f983b2013-09-05 16:07:26 -0700250 public MultiMap<PsiElement, DfaMemoryState> getNestedClosures() {
251 return new MultiMap<PsiElement, DfaMemoryState>(myNestedClosures);
252 }
253
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700254 public Pair<Set<Instruction>,Set<Instruction>> getConstConditionalExpressions() {
255 Set<Instruction> trueSet = new HashSet<Instruction>();
256 Set<Instruction> falseSet = new HashSet<Instruction>();
257
258 for (Instruction instruction : myInstructions) {
259 if (instruction instanceof BranchingInstruction) {
260 BranchingInstruction branchingInstruction = (BranchingInstruction)instruction;
261 if (branchingInstruction.getPsiAnchor() != null && branchingInstruction.isConditionConst()) {
262 if (!branchingInstruction.isTrueReachable()) {
263 falseSet.add(branchingInstruction);
264 }
265
266 if (!branchingInstruction.isFalseReachable()) {
267 trueSet.add(branchingInstruction);
268 }
269 }
270 }
271 }
272
273 for (Instruction instruction : myInstructions) {
274 if (instruction instanceof BranchingInstruction) {
275 BranchingInstruction branchingInstruction = (BranchingInstruction)instruction;
276 if (branchingInstruction.isTrueReachable()) {
277 falseSet.remove(branchingInstruction);
278 }
279 if (branchingInstruction.isFalseReachable()) {
280 trueSet.remove(branchingInstruction);
281 }
282 }
283 }
284
285 return Pair.create(trueSet, falseSet);
286 }
287
Tor Norbyec7f983b2013-09-05 16:07:26 -0700288 private DfaMemoryStateImpl createClosureState(DfaMemoryState memState) {
289 DfaMemoryStateImpl copy = (DfaMemoryStateImpl)memState.createCopy();
290 copy.flushFields(getFields());
291 Set<DfaVariableValue> vars = new HashSet<DfaVariableValue>(copy.getVariableStates().keySet());
292 for (DfaVariableValue value : vars) {
293 copy.flushDependencies(value);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700294 }
Tor Norbyebeca9832013-09-19 12:39:22 -0700295 copy.emptyStack();
Tor Norbyec7f983b2013-09-05 16:07:26 -0700296 return copy;
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700297 }
298}