blob: 6c5c9096fac155b61f82314353adbfd23e192e03 [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
Tor Norbyef7998d02013-09-27 10:19:19 -070027import com.intellij.codeInspection.dataFlow.instructions.*;
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070028import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
29import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
30import com.intellij.openapi.application.ApplicationManager;
31import com.intellij.openapi.diagnostic.Logger;
32import com.intellij.openapi.progress.ProgressManager;
Tor Norbye3a2425a52013-11-04 10:16:08 -080033import com.intellij.openapi.util.Condition;
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070034import com.intellij.openapi.util.Key;
35import com.intellij.openapi.util.Pair;
36import com.intellij.psi.*;
37import com.intellij.psi.util.PsiTreeUtil;
38import com.intellij.psi.util.PsiUtil;
Tor Norbyef7998d02013-09-27 10:19:19 -070039import com.intellij.util.containers.ContainerUtil;
Tor Norbyec7f983b2013-09-05 16:07:26 -070040import com.intellij.util.containers.MultiMap;
Tor Norbyef7998d02013-09-27 10:19:19 -070041import com.intellij.util.containers.MultiMapBasedOnSet;
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;
Tor Norbye3a2425a52013-11-04 10:16:08 -080055 private final DfaValueFactory myValueFactory;
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070056
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 Norbye3a2425a52013-11-04 10:16:08 -080061 protected DataFlowRunner(PsiElement block) {
62 PsiElement parentConstructor = PsiTreeUtil.findFirstParent(block, new Condition<PsiElement>() {
63 @Override
64 public boolean value(PsiElement psiElement) {
65 return psiElement instanceof PsiMethod && ((PsiMethod)psiElement).isConstructor();
66 }
67 });
68 myValueFactory = new DfaValueFactory(parentConstructor == null);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070069 }
70
71 public DfaValueFactory getFactory() {
72 return myValueFactory;
73 }
74
Tor Norbyec7f983b2013-09-05 16:07:26 -070075 protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable<DfaMemoryState> initialStates) {
76 }
77
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070078 @Nullable
Tor Norbyec7f983b2013-09-05 16:07:26 -070079 private Collection<DfaMemoryState> createInitialStates(@NotNull PsiElement psiBlock, InstructionVisitor visitor) {
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070080 PsiClass containingClass = PsiTreeUtil.getParentOfType(psiBlock, PsiClass.class);
81 if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) {
82 final PsiElement parent = containingClass.getParent();
83 final PsiCodeBlock block = DfaPsiUtil.getTopmostBlockInSameClass(parent);
84 if ((parent instanceof PsiNewExpression || parent instanceof PsiDeclarationStatement) && block != null) {
Tor Norbyec7f983b2013-09-05 16:07:26 -070085 final RunnerResult result = analyzeMethod(block, visitor);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070086 if (result == RunnerResult.OK) {
Tor Norbyec7f983b2013-09-05 16:07:26 -070087 final Collection<DfaMemoryState> closureStates = myNestedClosures.get(DfaPsiUtil.getTopmostBlockInSameClass(psiBlock));
Tor Norbyeec3fb1e2013-05-31 07:45:51 -070088 if (!closureStates.isEmpty()) {
89 return closureStates;
90 }
91 }
92 return null;
93 }
94 }
95
96 return Arrays.asList(createMemoryState());
97 }
98
99 public final RunnerResult analyzeMethod(@NotNull PsiElement psiBlock, InstructionVisitor visitor) {
Tor Norbyec7f983b2013-09-05 16:07:26 -0700100 Collection<DfaMemoryState> initialStates = createInitialStates(psiBlock, visitor);
101 return initialStates == null ? RunnerResult.NOT_APPLICABLE : analyzeMethod(psiBlock, visitor, false, initialStates);
Tor Norbye9a345242013-07-08 10:50:41 -0700102 }
103
Tor Norbyec7f983b2013-09-05 16:07:26 -0700104 public final RunnerResult analyzeMethod(@NotNull PsiElement psiBlock,
105 InstructionVisitor visitor,
106 boolean ignoreAssertions,
107 @NotNull Collection<DfaMemoryState> initialStates) {
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700108 try {
Tor Norbyec7f983b2013-09-05 16:07:26 -0700109 prepareAnalysis(psiBlock, initialStates);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700110
Tor Norbye9a345242013-07-08 10:50:41 -0700111 final ControlFlow flow = createControlFlowAnalyzer().buildControlFlow(psiBlock, ignoreAssertions);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700112 if (flow == null) return RunnerResult.NOT_APPLICABLE;
113
114 int endOffset = flow.getInstructionCount();
115 myInstructions = flow.getInstructions();
116 myFields = flow.getFields();
Tor Norbyec7f983b2013-09-05 16:07:26 -0700117 myNestedClosures.clear();
Tor Norbyef7998d02013-09-27 10:19:19 -0700118
119 Set<Instruction> joinInstructions = ContainerUtil.newHashSet();
120 for (Instruction instruction : myInstructions) {
121 if (instruction instanceof GotoInstruction) {
122 joinInstructions.add(myInstructions[((GotoInstruction)instruction).getOffset()]);
123 } else if (instruction instanceof ConditionalGotoInstruction) {
124 joinInstructions.add(myInstructions[((ConditionalGotoInstruction)instruction).getOffset()]);
Tor Norbyef7998d02013-09-27 10:19:19 -0700125 }
126 }
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700127
128 if (LOG.isDebugEnabled()) {
129 LOG.debug("Analyzing code block: " + psiBlock.getText());
130 for (int i = 0; i < myInstructions.length; i++) {
Tor Norbye76753662013-10-15 19:49:27 -0700131 LOG.debug(i + ": " + myInstructions[i].toString());
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700132 }
133 }
Tor Norbye76753662013-10-15 19:49:27 -0700134 //for (int i = 0; i < myInstructions.length; i++) System.out.println(i + ": " + myInstructions[i].toString());
135
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700136 Integer tooExpensiveHash = psiBlock.getUserData(TOO_EXPENSIVE_HASH);
137 if (tooExpensiveHash != null && tooExpensiveHash == psiBlock.getText().hashCode()) {
138 LOG.debug("Too complex because hasn't changed since being too complex already");
139 return RunnerResult.TOO_COMPLEX;
140 }
141
Tor Norbyef7998d02013-09-27 10:19:19 -0700142 final StateQueue queue = new StateQueue();
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700143 for (final DfaMemoryState initialState : initialStates) {
Tor Norbyef7998d02013-09-27 10:19:19 -0700144 queue.offer(new DfaInstructionState(myInstructions[0], initialState));
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700145 }
146
Tor Norbyef7998d02013-09-27 10:19:19 -0700147 MultiMapBasedOnSet<BranchingInstruction, DfaMemoryState> processedStates = new MultiMapBasedOnSet<BranchingInstruction, DfaMemoryState>();
148 MultiMapBasedOnSet<BranchingInstruction, DfaMemoryState> incomingStates = new MultiMapBasedOnSet<BranchingInstruction, DfaMemoryState>();
149
Tor Norbyebeca9832013-09-19 12:39:22 -0700150 WorkingTimeMeasurer measurer = new WorkingTimeMeasurer(shouldCheckTimeLimit() ? ourTimeLimit : ourTimeLimit * 42);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700151 int count = 0;
152 while (!queue.isEmpty()) {
Tor Norbyef7998d02013-09-27 10:19:19 -0700153 for (DfaInstructionState instructionState : queue.getNextInstructionStates(joinInstructions)) {
154 if (count++ % 1024 == 0 && measurer.isTimeOver()) {
155 LOG.debug("Too complex because the analysis took too long");
156 psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode());
157 return RunnerResult.TOO_COMPLEX;
158 }
159 ProgressManager.checkCanceled();
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700160
Tor Norbyef7998d02013-09-27 10:19:19 -0700161 if (LOG.isDebugEnabled()) {
162 LOG.debug(instructionState.toString());
163 }
164 //System.out.println(instructionState.toString());
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700165
Tor Norbyef7998d02013-09-27 10:19:19 -0700166 Instruction instruction = instructionState.getInstruction();
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700167
Tor Norbyef7998d02013-09-27 10:19:19 -0700168 if (instruction instanceof BranchingInstruction) {
169 BranchingInstruction branching = (BranchingInstruction)instruction;
Tor Norbye32218cc2013-10-08 09:48:09 -0700170 Collection<DfaMemoryState> processed = processedStates.get(branching);
171 if (processed.contains(instructionState.getMemoryState())) {
Tor Norbyef7998d02013-09-27 10:19:19 -0700172 continue;
173 }
Tor Norbye32218cc2013-10-08 09:48:09 -0700174 if (processed.size() > MAX_STATES_PER_BRANCH) {
Tor Norbyef7998d02013-09-27 10:19:19 -0700175 LOG.debug("Too complex because too many different possible states");
176 return RunnerResult.TOO_COMPLEX; // Too complex :(
177 }
178 processedStates.putValue(branching, instructionState.getMemoryState().createCopy());
179 }
180
181 DfaInstructionState[] after = acceptInstruction(visitor, instructionState);
182 for (DfaInstructionState state : after) {
183 Instruction nextInstruction = state.getInstruction();
184 if (nextInstruction.getIndex() >= endOffset) {
185 continue;
186 }
187 if (nextInstruction instanceof BranchingInstruction) {
188 BranchingInstruction branching = (BranchingInstruction)nextInstruction;
189 if (processedStates.get(branching).contains(state.getMemoryState()) ||
190 incomingStates.get(branching).contains(state.getMemoryState())) {
191 continue;
192 }
193 incomingStates.putValue(branching, state.getMemoryState().createCopy());
194 }
195 queue.offer(state);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700196 }
197 }
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700198 }
199
200 psiBlock.putUserData(TOO_EXPENSIVE_HASH, null);
201 LOG.debug("Analysis ok");
202 return RunnerResult.OK;
203 }
204 catch (ArrayIndexOutOfBoundsException e) {
Tor Norbye76753662013-10-15 19:49:27 -0700205 LOG.error(psiBlock.getText(), e);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700206 return RunnerResult.ABORTED;
207 }
208 catch (EmptyStackException e) {
Tor Norbye76753662013-10-15 19:49:27 -0700209 LOG.error(psiBlock.getText(), e);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700210 return RunnerResult.ABORTED;
211 }
212 }
213
Tor Norbyebeca9832013-09-19 12:39:22 -0700214 protected boolean shouldCheckTimeLimit() {
215 return !ApplicationManager.getApplication().isUnitTestMode();
216 }
217
Tor Norbye3a2425a52013-11-04 10:16:08 -0800218 protected DfaInstructionState[] acceptInstruction(InstructionVisitor visitor, DfaInstructionState instructionState) {
Tor Norbyec7f983b2013-09-05 16:07:26 -0700219 Instruction instruction = instructionState.getInstruction();
220 if (instruction instanceof MethodCallInstruction) {
221 PsiCallExpression anchor = ((MethodCallInstruction)instruction).getCallExpression();
222 if (anchor instanceof PsiNewExpression) {
223 PsiAnonymousClass anonymousClass = ((PsiNewExpression)anchor).getAnonymousClass();
224 if (anonymousClass != null) {
225 registerNestedClosures(instructionState, anonymousClass);
226 }
227 }
228 }
Tor Norbye8668e1b2013-12-20 09:14:04 -0800229 else if (instruction instanceof LambdaInstruction) {
230 PsiLambdaExpression lambdaExpression = ((LambdaInstruction)instruction).getLambdaExpression();
231 registerNestedClosures(instructionState, lambdaExpression);
232 }
Tor Norbyec7f983b2013-09-05 16:07:26 -0700233 else if (instruction instanceof EmptyInstruction) {
234 PsiElement anchor = ((EmptyInstruction)instruction).getAnchor();
235 if (anchor instanceof PsiDeclarationStatement) {
236 for (PsiElement element : ((PsiDeclarationStatement)anchor).getDeclaredElements()) {
237 if (element instanceof PsiClass) {
238 registerNestedClosures(instructionState, (PsiClass)element);
239 }
240 }
241 }
242 }
243
244 return instruction.accept(this, instructionState.getMemoryState(), visitor);
245 }
246
247 private void registerNestedClosures(DfaInstructionState instructionState, PsiClass nestedClass) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700248 DfaMemoryState state = instructionState.getMemoryState();
Tor Norbyec7f983b2013-09-05 16:07:26 -0700249 for (PsiMethod method : nestedClass.getMethods()) {
250 PsiCodeBlock body = method.getBody();
251 if (body != null) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700252 myNestedClosures.putValue(body, createClosureState(state));
Tor Norbyec7f983b2013-09-05 16:07:26 -0700253 }
254 }
255 for (PsiClassInitializer initializer : nestedClass.getInitializers()) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700256 myNestedClosures.putValue(initializer.getBody(), createClosureState(state));
Tor Norbyec7f983b2013-09-05 16:07:26 -0700257 }
258 for (PsiField field : nestedClass.getFields()) {
Tor Norbyebeca9832013-09-19 12:39:22 -0700259 myNestedClosures.putValue(field, createClosureState(state));
Tor Norbyec7f983b2013-09-05 16:07:26 -0700260 }
261 }
Tor Norbye8668e1b2013-12-20 09:14:04 -0800262
263 private void registerNestedClosures(DfaInstructionState instructionState, PsiLambdaExpression expr) {
264 DfaMemoryState state = instructionState.getMemoryState();
265 PsiElement body = expr.getBody();
266 if (body != null) {
267 myNestedClosures.putValue(body, createClosureState(state));
268 }
269 }
Tor Norbyec7f983b2013-09-05 16:07:26 -0700270
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700271 protected ControlFlowAnalyzer createControlFlowAnalyzer() {
272 return new ControlFlowAnalyzer(myValueFactory);
273 }
274
275 protected DfaMemoryState createMemoryState() {
276 return new DfaMemoryStateImpl(myValueFactory);
277 }
278
279 public Instruction[] getInstructions() {
280 return myInstructions;
281 }
282
Tor Norbyec7f983b2013-09-05 16:07:26 -0700283 public Instruction getInstruction(int index) {
284 return myInstructions[index];
285 }
286
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700287 public DfaVariableValue[] getFields() {
288 return myFields;
289 }
290
Tor Norbyec7f983b2013-09-05 16:07:26 -0700291 public MultiMap<PsiElement, DfaMemoryState> getNestedClosures() {
292 return new MultiMap<PsiElement, DfaMemoryState>(myNestedClosures);
293 }
294
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700295 public Pair<Set<Instruction>,Set<Instruction>> getConstConditionalExpressions() {
296 Set<Instruction> trueSet = new HashSet<Instruction>();
297 Set<Instruction> falseSet = new HashSet<Instruction>();
298
299 for (Instruction instruction : myInstructions) {
300 if (instruction instanceof BranchingInstruction) {
301 BranchingInstruction branchingInstruction = (BranchingInstruction)instruction;
302 if (branchingInstruction.getPsiAnchor() != null && branchingInstruction.isConditionConst()) {
303 if (!branchingInstruction.isTrueReachable()) {
304 falseSet.add(branchingInstruction);
305 }
306
307 if (!branchingInstruction.isFalseReachable()) {
308 trueSet.add(branchingInstruction);
309 }
310 }
311 }
312 }
313
314 for (Instruction instruction : myInstructions) {
315 if (instruction instanceof BranchingInstruction) {
316 BranchingInstruction branchingInstruction = (BranchingInstruction)instruction;
317 if (branchingInstruction.isTrueReachable()) {
318 falseSet.remove(branchingInstruction);
319 }
320 if (branchingInstruction.isFalseReachable()) {
321 trueSet.remove(branchingInstruction);
322 }
323 }
324 }
325
326 return Pair.create(trueSet, falseSet);
327 }
328
Tor Norbyec7f983b2013-09-05 16:07:26 -0700329 private DfaMemoryStateImpl createClosureState(DfaMemoryState memState) {
330 DfaMemoryStateImpl copy = (DfaMemoryStateImpl)memState.createCopy();
331 copy.flushFields(getFields());
332 Set<DfaVariableValue> vars = new HashSet<DfaVariableValue>(copy.getVariableStates().keySet());
333 for (DfaVariableValue value : vars) {
334 copy.flushDependencies(value);
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700335 }
Tor Norbyebeca9832013-09-19 12:39:22 -0700336 copy.emptyStack();
Tor Norbyec7f983b2013-09-05 16:07:26 -0700337 return copy;
Tor Norbyeec3fb1e2013-05-31 07:45:51 -0700338 }
339}