@Override public boolean isValueCompatible() { final PsiElement body = getBody(); if (body != null) { try { final ControlFlow controlFlow = ControlFlowFactory.getInstance(getProject()) .getControlFlow( body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); if (ControlFlowUtil.findExitPointsAndStatements( controlFlow, 0, controlFlow.getSize(), new IntArrayList(), PsiReturnStatement.class, PsiThrowStatement.class) .isEmpty()) { return false; } } catch (AnalysisCanceledException e) { return true; } } return true; }
private static void checkCodeBlock( final PsiCodeBlock body, final Set<PsiField> candidates, Set<PsiField> usedFields) { try { final ControlFlow controlFlow = ControlFlowFactory.getInstance(body.getProject()) .getControlFlow(body, AllVariablesControlFlowPolicy.getInstance()); final List<PsiVariable> usedVars = ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize()); for (PsiVariable usedVariable : usedVars) { if (usedVariable instanceof PsiField) { final PsiField usedField = (PsiField) usedVariable; if (!usedFields.add(usedField)) { candidates.remove(usedField); // used in more than one code block } } } final List<PsiReferenceExpression> readBeforeWrites = ControlFlowUtil.getReadBeforeWrite(controlFlow); for (final PsiReferenceExpression readBeforeWrite : readBeforeWrites) { final PsiElement resolved = readBeforeWrite.resolve(); if (resolved instanceof PsiField) { final PsiField field = (PsiField) resolved; PsiElement parent = body.getParent(); if (!(parent instanceof PsiMethod) || !((PsiMethod) parent).isConstructor() || field.getInitializer() == null || field.hasModifierProperty(PsiModifier.STATIC)) { candidates.remove(field); } } } } catch (AnalysisCanceledException e) { candidates.clear(); } }
public ControlFlow findControlFlow(Node source, Node destination) { for (ControlFlow c : this.getControlFlows()) { if (c.getSource().equals(source) && c.getDestination().equals(destination)) { return c; } } return null; }
public ControlFlowWrapper(Project project, PsiElement codeFragment, PsiElement[] elements) throws PrepareFailedException { try { myControlFlow = ControlFlowFactory.getInstance(project) .getControlFlow( codeFragment, new LocalsControlFlowPolicy(codeFragment), false, false); } catch (AnalysisCanceledException e) { throw new PrepareFailedException( RefactoringBundle.message("extract.method.control.flow.analysis.failed"), e.getErrorElement()); } if (LOG.isDebugEnabled()) { LOG.debug(myControlFlow.toString()); } int flowStart = -1; int index = 0; while (index < elements.length) { flowStart = myControlFlow.getStartOffset(elements[index]); if (flowStart >= 0) break; index++; } int flowEnd; if (flowStart < 0) { // no executable code flowStart = 0; flowEnd = 0; } else { index = elements.length - 1; while (true) { flowEnd = myControlFlow.getEndOffset(elements[index]); if (flowEnd >= 0) break; index--; } } myFlowStart = flowStart; myFlowEnd = flowEnd; if (LOG.isDebugEnabled()) { LOG.debug("start offset:" + myFlowStart); LOG.debug("end offset:" + myFlowEnd); } }
public PsiStatement getExitStatementCopy( PsiElement returnStatement, final PsiElement[] elements) { PsiStatement exitStatementCopy = null; // replace all exit-statements such as break's or continue's with appropriate return for (PsiStatement exitStatement : myExitStatements) { if (exitStatement instanceof PsiReturnStatement) { if (!myGenerateConditionalExit) continue; } else if (exitStatement instanceof PsiBreakStatement) { PsiStatement statement = ((PsiBreakStatement) exitStatement).findExitedStatement(); if (statement == null) continue; int startOffset = myControlFlow.getStartOffset(statement); int endOffset = myControlFlow.getEndOffset(statement); if (myFlowStart <= startOffset && endOffset <= myFlowEnd) continue; } else if (exitStatement instanceof PsiContinueStatement) { PsiStatement statement = ((PsiContinueStatement) exitStatement).findContinuedStatement(); if (statement == null) continue; int startOffset = myControlFlow.getStartOffset(statement); int endOffset = myControlFlow.getEndOffset(statement); if (myFlowStart <= startOffset && endOffset <= myFlowEnd) continue; } else { LOG.error(String.valueOf(exitStatement)); continue; } int index = -1; for (int j = 0; j < elements.length; j++) { if (exitStatement.equals(elements[j])) { index = j; break; } } if (exitStatementCopy == null) { if (needExitStatement(exitStatement)) { exitStatementCopy = (PsiStatement) exitStatement.copy(); } } PsiElement result = exitStatement.replace(returnStatement); if (index >= 0) { elements[index] = result; } } return exitStatementCopy; }
@Override public boolean isVoidCompatible() { final PsiElement body = getBody(); if (body != null) { try { ControlFlow controlFlow = ControlFlowFactory.getInstance(getProject()) .getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance()); int startOffset = controlFlow.getStartOffset(body); int endOffset = controlFlow.getEndOffset(body); return startOffset != -1 && endOffset != -1 && !ControlFlowUtil.canCompleteNormally(controlFlow, startOffset, endOffset); } catch (AnalysisCanceledException e) { return true; } } return true; }
private boolean needExitStatement(final PsiStatement exitStatement) { if (exitStatement instanceof PsiContinueStatement) { // IDEADEV-11748 PsiStatement statement = ((PsiContinueStatement) exitStatement).findContinuedStatement(); if (statement == null) return true; if (statement instanceof PsiLoopStatement) statement = ((PsiLoopStatement) statement).getBody(); int endOffset = myControlFlow.getEndOffset(statement); return endOffset > myFlowEnd; } return true; }
@Nullable static HighlightInfo checkInitializerCompleteNormally(@NotNull PsiClassInitializer initializer) { final PsiCodeBlock body = initializer.getBody(); // unhandled exceptions already reported try { final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body); final int completionReasons = ControlFlowUtil.getCompletionReasons(controlFlow, 0, controlFlow.getSize()); if (!BitUtil.isSet(completionReasons, ControlFlowUtil.NORMAL_COMPLETION_REASON)) { String description = JavaErrorMessages.message("initializer.must.be.able.to.complete.normally"); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(body) .descriptionAndTooltip(description) .create(); } } catch (AnalysisCanceledException e) { // incomplete code } return null; }
private void removeParametersUsedInExitsOnly( PsiElement codeFragment, List<PsiVariable> inputVariables) { LocalSearchScope scope = new LocalSearchScope(codeFragment); Variables: for (Iterator<PsiVariable> iterator = inputVariables.iterator(); iterator.hasNext(); ) { PsiVariable variable = iterator.next(); for (PsiReference ref : ReferencesSearch.search(variable, scope)) { PsiElement element = ref.getElement(); int elementOffset = myControlFlow.getStartOffset(element); if (elementOffset == -1) { continue Variables; } if (elementOffset >= myFlowStart && elementOffset <= myFlowEnd) { if (!isInExitStatements(element, myExitStatements)) continue Variables; } } iterator.remove(); } }
public Collection<PsiStatement> prepareExitStatements( final @NotNull PsiElement[] elements, final @NotNull PsiElement enclosingCodeFragment) throws ExitStatementsNotSameException { myExitPoints = new IntArrayList(); myExitStatements = ControlFlowUtil.findExitPointsAndStatements( myControlFlow, myFlowStart, myFlowEnd, myExitPoints, ControlFlowUtil.DEFAULT_EXIT_STATEMENTS_CLASSES); if (ControlFlowUtil.hasObservableThrowExitPoints( myControlFlow, myFlowStart, myFlowEnd, elements, enclosingCodeFragment)) { throw new ExitStatementsNotSameException(); } if (LOG.isDebugEnabled()) { LOG.debug("exit points:"); for (int i = 0; i < myExitPoints.size(); i++) { LOG.debug(" " + myExitPoints.get(i)); } LOG.debug("exit statements:"); for (PsiStatement exitStatement : myExitStatements) { LOG.debug(" " + exitStatement); } } if (myExitPoints.isEmpty()) { // if the fragment never exits assume as if it exits in the end myExitPoints.add(myControlFlow.getEndOffset(elements[elements.length - 1])); } if (myExitPoints.size() != 1) { myGenerateConditionalExit = true; areExitStatementsTheSame(); } return myExitStatements; }
/** * Can emulate the execution flow of multiple threads in a deterministic way so it is easy to * emulate race conditions or deadlocks just with the use of additional log messages inserted into * the code. * * <p>The best example showing usage of this method is real life test. Read <a * href="http://hg.netbeans.org/main/raw-file/tip/nbjunit/test/unit/src/org/netbeans/junit/FlowControlTest.java">FlowControlTest.java</a> * to know everything about the expected usage of this method. * * <p>The method does listen on output send to a logger <code>listenTo</code> by various threads * and either suspends them or wake them up trying as best as it can to mimic the log output * described in <code>order</code>. Of course, this may not always be possible, so there is the * <code>timeout</code> value which specifies the maximum time a thread can be suspended while * waiting for a single message. The information about the internal behaviour of the controlFlow * method can be send to <code>reportTo</code> logger, if provided, so in case of failure one can * analyse what went wrong. * * <p>The format of the order is a set of lines like: * * <pre> * THREAD:name_of_the_thread MSG:message_to_expect * </pre> * * which define the order saying that at this time a thread with a given name is expected to send * given message. Both the name of the thread and the message are regular expressions so one can * shorten them by using <q>.*</q> or any other trick. Btw. the format of the <code>order</code> * is similar to the one logged by the {@link Log#enable} or {@link NbTestCase#logLevel} methods, * so when one gets a test failure with enabled logging, it is enough to just delete the * unnecessary messages, replace too specific texts like <code>@574904</code> with <code>.*</code> * and the order is ready for use. * * @param listenTo the logger to listen to and guide the execution according to messages sent to * it * @param reportTo the logger to report internal state to or <code>null</code> if the logging is * not needed * @param order the string describing the expected execution order of threads * @param timeout the maximal wait time of each thread on given message, zero if the waiting shall * be infinite * @author Jaroslav Tulach, invented during year 2005 * @since 1.28 */ public static void controlFlow(Logger listenTo, Logger reportTo, String order, int timeout) { ControlFlow.registerSwitches(listenTo, reportTo, order, timeout); }
public final RunnerResult analyzeMethod( @NotNull PsiElement psiBlock, InstructionVisitor visitor, boolean ignoreAssertions, @NotNull Collection<DfaMemoryState> initialStates) { try { prepareAnalysis(psiBlock, initialStates); final ControlFlow flow = createControlFlowAnalyzer().buildControlFlow(psiBlock, ignoreAssertions); if (flow == null) return RunnerResult.NOT_APPLICABLE; int endOffset = flow.getInstructionCount(); myInstructions = flow.getInstructions(); myFields = flow.getFields(); myNestedClosures.clear(); if (LOG.isDebugEnabled()) { LOG.debug("Analyzing code block: " + psiBlock.getText()); for (int i = 0; i < myInstructions.length; i++) { Instruction instruction = myInstructions[i]; LOG.debug(i + ": " + instruction.toString()); } } Integer tooExpensiveHash = psiBlock.getUserData(TOO_EXPENSIVE_HASH); if (tooExpensiveHash != null && tooExpensiveHash == psiBlock.getText().hashCode()) { LOG.debug("Too complex because hasn't changed since being too complex already"); return RunnerResult.TOO_COMPLEX; } final ArrayList<DfaInstructionState> queue = new ArrayList<DfaInstructionState>(); for (final DfaMemoryState initialState : initialStates) { queue.add(new DfaInstructionState(myInstructions[0], initialState)); } long timeLimit = ourTimeLimit; final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode(); WorkingTimeMeasurer measurer = new WorkingTimeMeasurer(timeLimit); int count = 0; while (!queue.isEmpty()) { if (count % 50 == 0 && !unitTestMode && measurer.isTimeOver()) { LOG.debug("Too complex because the analysis took too long"); psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode()); return RunnerResult.TOO_COMPLEX; } ProgressManager.checkCanceled(); DfaInstructionState instructionState = queue.remove(0); if (LOG.isDebugEnabled()) { LOG.debug(instructionState.toString()); } // System.out.println(instructionState.toString()); Instruction instruction = instructionState.getInstruction(); long distance = instructionState.getDistanceFromStart(); if (instruction instanceof BranchingInstruction) { if (!instruction.setMemoryStateProcessed( instructionState.getMemoryState().createCopy())) { LOG.debug("Too complex because too many different possible states"); return RunnerResult.TOO_COMPLEX; // Too complex :( } } DfaInstructionState[] after = acceptInstruction(visitor, instructionState); for (DfaInstructionState state : after) { Instruction nextInstruction = state.getInstruction(); if ((!(nextInstruction instanceof BranchingInstruction) || !nextInstruction.isMemoryStateProcessed(state.getMemoryState())) && instruction.getIndex() < endOffset) { state.setDistanceFromStart(distance + 1); queue.add(state); } } count++; } psiBlock.putUserData(TOO_EXPENSIVE_HASH, null); LOG.debug("Analysis ok"); return RunnerResult.OK; } catch (ArrayIndexOutOfBoundsException e) { LOG.error(psiBlock.getText(), e); // TODO fix in better times return RunnerResult.ABORTED; } catch (EmptyStackException e) { if (LOG.isDebugEnabled()) { LOG.error(e); // TODO fix in better times } return RunnerResult.ABORTED; } }
public Collection<ControlFlowUtil.VariableInfo> getInitializedTwice(int start) { return ControlFlowUtil.getInitializedTwice(myControlFlow, start, myControlFlow.getSize()); }
public List<PsiVariable> getUsedVariables(int start) { return getUsedVariables(start, myControlFlow.getSize()); }
@Override public void onReferencesBuild(RefElement refElement) { if (refElement instanceof RefClass) { final PsiClass psiClass = (PsiClass) refElement.getElement(); if (psiClass != null) { if (refElement.isEntry()) { ((RefClassImpl) refElement).setFlag(false, CAN_BE_FINAL_MASK); } PsiMethod[] psiMethods = psiClass.getMethods(); PsiField[] psiFields = psiClass.getFields(); HashSet<PsiVariable> allFields = new HashSet<PsiVariable>(); ContainerUtil.addAll(allFields, psiFields); ArrayList<PsiVariable> instanceInitializerInitializedFields = new ArrayList<PsiVariable>(); boolean hasInitializers = false; for (PsiClassInitializer initializer : psiClass.getInitializers()) { PsiCodeBlock body = initializer.getBody(); hasInitializers = true; ControlFlow flow; try { flow = ControlFlowFactory.getInstance(body.getProject()) .getControlFlow( body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); } catch (AnalysisCanceledException e) { flow = ControlFlow.EMPTY; } Collection<PsiVariable> writtenVariables = new ArrayList<PsiVariable>(); ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false, writtenVariables); for (PsiVariable psiVariable : writtenVariables) { if (allFields.contains(psiVariable)) { if (instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); instanceInitializerInitializedFields.remove(psiVariable); } else { instanceInitializerInitializedFields.add(psiVariable); } } } for (PsiVariable psiVariable : writtenVariables) { if (!instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); } } } for (PsiMethod psiMethod : psiMethods) { if (psiMethod.isConstructor()) { PsiCodeBlock body = psiMethod.getBody(); if (body != null) { hasInitializers = true; ControlFlow flow; try { flow = ControlFlowFactory.getInstance(body.getProject()) .getControlFlow( body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); } catch (AnalysisCanceledException e) { flow = ControlFlow.EMPTY; } Collection<PsiVariable> writtenVariables = ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false); for (PsiVariable psiVariable : writtenVariables) { if (instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); instanceInitializerInitializedFields.remove(psiVariable); } } List<PsiMethod> redirectedConstructors = HighlightControlFlowUtil.getChainedConstructors(psiMethod); if (redirectedConstructors == null || redirectedConstructors.isEmpty()) { List<PsiVariable> ssaVariables = ControlFlowUtil.getSSAVariables(flow); ArrayList<PsiVariable> good = new ArrayList<PsiVariable>(ssaVariables); good.addAll(instanceInitializerInitializedFields); allFields.retainAll(good); } else { allFields.removeAll(writtenVariables); } } } } for (PsiField psiField : psiFields) { if ((!hasInitializers || !allFields.contains(psiField)) && psiField.getInitializer() == null) { final RefFieldImpl refField = (RefFieldImpl) myManager.getReference(psiField); if (refField != null) { refField.setFlag(false, CAN_BE_FINAL_MASK); } } } } } else if (refElement instanceof RefMethod) { final RefMethod refMethod = (RefMethod) refElement; if (refMethod.isEntry()) { ((RefMethodImpl) refMethod).setFlag(false, CAN_BE_FINAL_MASK); } } }
@NotNull final RunnerResult analyzeMethod( @NotNull PsiElement psiBlock, @NotNull InstructionVisitor visitor, boolean ignoreAssertions, @NotNull Collection<DfaMemoryState> initialStates) { if (PsiTreeUtil.findChildOfType(psiBlock, OuterLanguageElement.class) != null) return RunnerResult.NOT_APPLICABLE; try { final ControlFlow flow = new ControlFlowAnalyzer(myValueFactory, psiBlock, ignoreAssertions).buildControlFlow(); if (flow == null) return RunnerResult.NOT_APPLICABLE; int[] loopNumber = LoopAnalyzer.calcInLoop(flow); int endOffset = flow.getInstructionCount(); myInstructions = flow.getInstructions(); myNestedClosures.clear(); Set<Instruction> joinInstructions = ContainerUtil.newHashSet(); for (int index = 0; index < myInstructions.length; index++) { Instruction instruction = myInstructions[index]; if (instruction instanceof GotoInstruction) { joinInstructions.add(myInstructions[((GotoInstruction) instruction).getOffset()]); } else if (instruction instanceof ConditionalGotoInstruction) { joinInstructions.add( myInstructions[((ConditionalGotoInstruction) instruction).getOffset()]); } else if (instruction instanceof MethodCallInstruction && !((MethodCallInstruction) instruction).getContracts().isEmpty()) { joinInstructions.add(myInstructions[index + 1]); } } if (LOG.isDebugEnabled()) { LOG.debug("Analyzing code block: " + psiBlock.getText()); for (int i = 0; i < myInstructions.length; i++) { LOG.debug(i + ": " + myInstructions[i]); } } // for (int i = 0; i < myInstructions.length; i++) System.out.println(i + ": " + // myInstructions[i].toString()); Integer tooExpensiveHash = psiBlock.getUserData(TOO_EXPENSIVE_HASH); if (tooExpensiveHash != null && tooExpensiveHash == psiBlock.getText().hashCode()) { LOG.debug("Too complex because hasn't changed since being too complex already"); return RunnerResult.TOO_COMPLEX; } final StateQueue queue = new StateQueue(); for (final DfaMemoryState initialState : initialStates) { queue.offer(new DfaInstructionState(myInstructions[0], initialState)); } MultiMap<BranchingInstruction, DfaMemoryState> processedStates = MultiMap.createSet(); MultiMap<BranchingInstruction, DfaMemoryState> incomingStates = MultiMap.createSet(); long msLimit = shouldCheckTimeLimit() ? Registry.intValue("ide.dfa.time.limit.online") : Registry.intValue("ide.dfa.time.limit.offline"); WorkingTimeMeasurer measurer = new WorkingTimeMeasurer(msLimit * 1000 * 1000); int count = 0; while (!queue.isEmpty()) { List<DfaInstructionState> states = queue.getNextInstructionStates(joinInstructions); for (DfaInstructionState instructionState : states) { if (count++ % 1024 == 0 && measurer.isTimeOver()) { LOG.debug("Too complex because the analysis took too long"); psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode()); return RunnerResult.TOO_COMPLEX; } ProgressManager.checkCanceled(); if (LOG.isDebugEnabled()) { LOG.debug(instructionState.toString()); } // System.out.println(instructionState.toString()); Instruction instruction = instructionState.getInstruction(); if (instruction instanceof BranchingInstruction) { BranchingInstruction branching = (BranchingInstruction) instruction; Collection<DfaMemoryState> processed = processedStates.get(branching); if (processed.contains(instructionState.getMemoryState())) { continue; } if (processed.size() > MAX_STATES_PER_BRANCH) { LOG.debug("Too complex because too many different possible states"); return RunnerResult.TOO_COMPLEX; // Too complex :( } if (loopNumber[branching.getIndex()] != 0) { processedStates.putValue(branching, instructionState.getMemoryState().createCopy()); } } DfaInstructionState[] after = acceptInstruction(visitor, instructionState); for (DfaInstructionState state : after) { Instruction nextInstruction = state.getInstruction(); if (nextInstruction.getIndex() >= endOffset) { continue; } handleStepOutOfLoop( instruction, nextInstruction, loopNumber, processedStates, incomingStates, states, after, queue); if (nextInstruction instanceof BranchingInstruction) { BranchingInstruction branching = (BranchingInstruction) nextInstruction; if (processedStates.get(branching).contains(state.getMemoryState()) || incomingStates.get(branching).contains(state.getMemoryState())) { continue; } if (loopNumber[branching.getIndex()] != 0) { incomingStates.putValue(branching, state.getMemoryState().createCopy()); } } queue.offer(state); } } } psiBlock.putUserData(TOO_EXPENSIVE_HASH, null); LOG.debug("Analysis ok"); return RunnerResult.OK; } catch (ArrayIndexOutOfBoundsException e) { LOG.error(psiBlock.getText(), e); return RunnerResult.ABORTED; } catch (EmptyStackException e) { LOG.error(psiBlock.getText(), e); return RunnerResult.ABORTED; } }