@Override public void visitUnaryExpression(GrUnaryExpression expression) { final GrExpression operand = expression.getOperand(); if (operand == null) return; if (expression.getOperationTokenType() != mLNOT) { operand.accept(this); visitCall(expression); return; } ConditionInstruction cond = new ConditionInstruction(expression); addNodeAndCheckPending(cond); registerCondition(cond); operand.accept(this); visitCall(expression); myConditions.removeFirstOccurrence(cond); List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression); InstructionImpl head = myHead; addNodeAndCheckPending(new PositiveGotoInstruction(expression, cond)); handlePossibleReturn(expression); addPendingEdge(expression, myHead); if (negations.isEmpty()) { myHead = head; } else { myHead = reduceAllNegationsIntoInstruction(expression, negations); } }
@NotNull private Set<VirtualFile> pollFilesToResolve() { Set<VirtualFile> set; synchronized (filesToResolve) { int queuedSize = filesToResolve.size(); set = new LinkedHashSet<VirtualFile>(queuedSize); // someone might have cleared this bit to mark file as processed for (VirtualFile file : filesToResolve) { if (fileIsInQueue.clear(getAbsId(file))) { set.add(file); } } filesToResolve.clear(); } return countAndMarkUnresolved(set, false); }
private InstructionImpl startNode(@Nullable GroovyPsiElement element, boolean checkPending) { final InstructionImpl instruction = new InstructionImpl(element); addNode(instruction); if (checkPending) checkPending(instruction); myProcessingStack.push(instruction); return instruction; }
@Nullable private InstructionImpl findInstruction(PsiElement element) { final Iterator<InstructionImpl> iterator = myProcessingStack.descendingIterator(); while (iterator.hasNext()) { final InstructionImpl instruction = iterator.next(); if (element.equals(instruction.getElement())) return instruction; } return null; }
@Override public void run() { while (!myDisposed) { boolean isEmpty; synchronized (filesToResolve) { isEmpty = filesToResolve.isEmpty(); } if (enableVetoes.get() > 0 || isEmpty || !resolveProcess.isDone() || HeavyProcessLatch.INSTANCE.isRunning() || PsiDocumentManager.getInstance(myProject).hasUncommitedDocuments()) { try { waitForQueue(); } catch (InterruptedException e) { break; } continue; } final Set<VirtualFile> files = pollFilesToResolve(); if (files.isEmpty()) continue; upToDate = false; myApplication.invokeLater( () -> { if (!resolveProcess.isDone()) return; log("Started to resolve " + files.size() + " files"); Task.Backgroundable backgroundable = new Task.Backgroundable(myProject, "Resolving files...", false) { @Override public void run(@NotNull final ProgressIndicator indicator) { if (!myApplication.isDisposed()) { processBatch(indicator, files); } } }; ProgressIndicator indicator; if (files.size() > 1) { // show progress indicator = new BackgroundableProcessIndicator(backgroundable); } else { indicator = new MyProgress(); } resolveProcess = ((ProgressManagerImpl) ProgressManager.getInstance()) .runProcessWithProgressAsynchronously(backgroundable, indicator, null); }, myProject.getDisposed()); flushLog(); } }
// return true if file was added to queue private boolean queueUpdate(@NotNull VirtualFile file) { synchronized (filesToResolve) { if (!(file instanceof VirtualFileWithId)) return false; int fileId = getAbsId(file); countAndMarkUnresolved(file, new LinkedHashSet<VirtualFile>(), true); boolean alreadyAdded = fileIsInQueue.set(fileId); if (!alreadyAdded) { filesToResolve.add(file); } upToDate = false; wakeUpUnderLock(); return !alreadyAdded; } }
@Nullable private ExceptionInfo findCatch(PsiType thrownType) { final Iterator<ExceptionInfo> iterator = myCaughtExceptionInfos.descendingIterator(); while (iterator.hasNext()) { final ExceptionInfo info = iterator.next(); final GrCatchClause clause = info.myClause; final GrParameter parameter = clause.getParameter(); if (parameter != null) { final PsiType type = parameter.getType(); if (type.isAssignableFrom(thrownType)) return info; } } return null; }
private void finishNode(InstructionImpl instruction) { final InstructionImpl popped = myProcessingStack.pop(); if (!instruction.equals(popped)) { String description = "popped: " + popped.toString() + " : " + popped.hashCode() + " , expected: " + instruction.toString() + " : " + instruction.hashCode(); error(description); } }
/** * Emulates throwing an exception from method call. Should be inserted into all places where * method or closure is called, because it can throw something unexpectedly */ private void visitCall(GroovyPsiElement call) { // optimization: don't add call instruction if there is no catch or finally block in the context if (myCaughtExceptionInfos.size() <= 0 && myFinallyCount <= 0) { return; } final InstructionImpl instruction = new ThrowingInstruction(call); addNodeAndCheckPending(instruction); for (ExceptionInfo info : myCaughtExceptionInfos) { info.myThrowers.add(instruction); } if (myFinallyCount > 0) { addPendingEdge(null, instruction); } }
private void processInstanceOf(GrExpression expression) { ConditionInstruction cond = new ConditionInstruction(expression); addNodeAndCheckPending(cond); registerCondition(cond); addNode(new InstanceOfInstruction(expression, cond)); NegatingGotoInstruction negation = new NegatingGotoInstruction(expression, cond); addNode(negation); handlePossibleReturn(expression); addPendingEdge(expression, negation); myHead = cond; addNode(new InstanceOfInstruction(expression, cond)); handlePossibleReturn(expression); myConditions.removeFirstOccurrence(cond); }
private void processBatch( @NotNull final ProgressIndicator indicator, @NotNull Set<VirtualFile> files) { assert !myApplication.isDispatchThread(); final int resolvedInPreviousBatch = this.resolvedInPreviousBatch; final int totalSize = files.size() + resolvedInPreviousBatch; final ConcurrentIntObjectMap<int[]> fileToForwardIds = ContainerUtil.createConcurrentIntObjectMap(); final Set<VirtualFile> toProcess = Collections.synchronizedSet(files); indicator.setIndeterminate(false); ProgressIndicatorUtils.forceWriteActionPriority(indicator, (Disposable) indicator); long start = System.currentTimeMillis(); Processor<VirtualFile> processor = file -> { double fraction = 1 - toProcess.size() * 1.0 / totalSize; indicator.setFraction(fraction); try { if (!file.isDirectory() && toResolve(file, myProject)) { int fileId = getAbsId(file); int i = totalSize - toProcess.size(); indicator.setText(i + "/" + totalSize + ": Resolving " + file.getPresentableUrl()); int[] forwardIds = processFile(file, fileId, indicator); if (forwardIds == null) { // queueUpdate(file); return false; } fileToForwardIds.put(fileId, forwardIds); } toProcess.remove(file); return true; } catch (RuntimeException e) { indicator.checkCanceled(); } return true; }; boolean success = true; try { success = processFilesConcurrently(files, indicator, processor); } finally { this.resolvedInPreviousBatch = toProcess.isEmpty() ? 0 : totalSize - toProcess.size(); queue(toProcess, "re-added after fail. success=" + success); storeIds(fileToForwardIds); long end = System.currentTimeMillis(); log( "Resolved batch of " + (totalSize - toProcess.size()) + " from " + totalSize + " files in " + ((end - start) / 1000) + "sec. (Gap: " + storage.gap + ")"); synchronized (filesToResolve) { upToDate = filesToResolve.isEmpty(); log("upToDate = " + upToDate); if (upToDate) { for (Listener listener : myListeners) { listener.allFilesResolved(); } } } } }
private void waitForQueue() throws InterruptedException { synchronized (filesToResolve) { filesToResolve.wait(1000); } }
private void wakeUpUnderLock() { filesToResolve.notifyAll(); }
public void visitTryStatement(GrTryCatchStatement tryCatchStatement) { final GrOpenBlock tryBlock = tryCatchStatement.getTryBlock(); final GrCatchClause[] catchClauses = tryCatchStatement.getCatchClauses(); final GrFinallyClause finallyClause = tryCatchStatement.getFinallyClause(); for (int i = catchClauses.length - 1; i >= 0; i--) { myCaughtExceptionInfos.push(new ExceptionInfo(catchClauses[i])); } if (finallyClause != null) myFinallyCount++; List<Pair<InstructionImpl, GroovyPsiElement>> oldPending = null; if (finallyClause != null) { oldPending = myPending; myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>(); } InstructionImpl tryBegin = startNode(tryBlock); tryBlock.accept(this); InstructionImpl tryEnd = myHead; finishNode(tryBegin); Set<Pair<InstructionImpl, GroovyPsiElement>> pendingAfterTry = new LinkedHashSet<Pair<InstructionImpl, GroovyPsiElement>>(myPending); @SuppressWarnings("unchecked") List<InstructionImpl>[] throwers = new List[catchClauses.length]; for (int i = 0; i < catchClauses.length; i++) { throwers[i] = myCaughtExceptionInfos.pop().myThrowers; } InstructionImpl[] catches = new InstructionImpl[catchClauses.length]; for (int i = 0; i < catchClauses.length; i++) { interruptFlow(); final InstructionImpl catchBeg = startNode(catchClauses[i]); for (InstructionImpl thrower : throwers[i]) { addEdge(thrower, catchBeg); } final GrParameter parameter = catchClauses[i].getParameter(); if (parameter != null && myPolicy.isVariableInitialized(parameter)) { addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, WRITE)); } catchClauses[i].accept(this); catches[i] = myHead; finishNode(catchBeg); } pendingAfterTry.addAll(myPending); myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>(pendingAfterTry); if (finallyClause != null) { myFinallyCount--; interruptFlow(); final InstructionImpl finallyInstruction = startNode(finallyClause, false); Set<AfterCallInstruction> postCalls = new LinkedHashSet<AfterCallInstruction>(); final List<Pair<InstructionImpl, GroovyPsiElement>> copy = myPending; myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>(); for (Pair<InstructionImpl, GroovyPsiElement> pair : copy) { postCalls.add(addCallNode(finallyInstruction, pair.getSecond(), pair.getFirst())); } if (tryEnd != null) { postCalls.add(addCallNode(finallyInstruction, tryCatchStatement, tryEnd)); } for (InstructionImpl catchEnd : catches) { if (catchEnd != null) { postCalls.add(addCallNode(finallyInstruction, tryCatchStatement, catchEnd)); } } // save added postcalls into separate list because we don't want returnInstruction grabbed // their pending edges List<Pair<InstructionImpl, GroovyPsiElement>> pendingPostCalls = myPending; myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>(); myHead = finallyInstruction; finallyClause.accept(this); final ReturnInstruction returnInstruction = new ReturnInstruction(finallyClause); for (AfterCallInstruction postCall : postCalls) { postCall.setReturnInstruction(returnInstruction); addEdge(returnInstruction, postCall); } addNodeAndCheckPending(returnInstruction); interruptFlow(); finishNode(finallyInstruction); if (oldPending == null) { error(); } oldPending.addAll(pendingPostCalls); myPending = oldPending; } else { if (tryEnd != null) { addPendingEdge(tryCatchStatement, tryEnd); } for (InstructionImpl catchEnd : catches) { addPendingEdge(tryBlock, catchEnd); } } }
@Override public void visitBinaryExpression(GrBinaryExpression expression) { final GrExpression left = expression.getLeftOperand(); final GrExpression right = expression.getRightOperand(); final IElementType opType = expression.getOperationTokenType(); if (ControlFlowBuilderUtil.isInstanceOfBinary(expression)) { expression.getLeftOperand().accept(this); processInstanceOf(expression); return; } if (opType != mLOR && opType != mLAND && opType != kIN) { left.accept(this); if (right != null) { right.accept(this); } visitCall(expression); return; } ConditionInstruction condition = new ConditionInstruction(expression); addNodeAndCheckPending(condition); registerCondition(condition); left.accept(this); if (right == null) return; final List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression); visitCall(expression); if (opType == mLAND) { InstructionImpl head = myHead; if (negations.isEmpty()) { addNode(new NegatingGotoInstruction(expression, condition)); handlePossibleReturn(expression); addPendingEdge(expression, myHead); } else { for (GotoInstruction negation : negations) { myHead = negation; handlePossibleReturn(expression); addPendingEdge(expression, myHead); } } myHead = head; } else /*if (opType == mLOR)*/ { final InstructionImpl instruction = addNodeAndCheckPending( new InstructionImpl(expression)); // collect all pending edges from left argument handlePossibleReturn(expression); addPendingEdge(expression, myHead); myHead = instruction; InstructionImpl head = reduceAllNegationsIntoInstruction(expression, negations); if (head != null) myHead = head; // addNode(new NegatingGotoInstruction(expression, myInstructionNumber++, condition)); } myConditions.removeFirstOccurrence(condition); right.accept(this); }
@Override public int getQueueSize() { synchronized (filesToResolve) { return filesToResolve.size(); } }
private void registerCondition(ConditionInstruction conditionStart) { for (ConditionInstruction condition : myConditions) { condition.addDependent(conditionStart); } myConditions.push(conditionStart); }