use of com.intellij.util.containers.IntArrayList in project intellij-community by JetBrains.
the class LineWrapperTest method doTest.
private void doTest(String text, boolean atLineStart, double clipWidth, int... expectedBreaks) {
IntArrayList actualBreaks = LineWrapper.calcBreakOffsets(text.toCharArray(), 0, text.length(), atLineStart, 0, clipWidth, myWidthProvider);
assertArrayEquals(expectedBreaks, actualBreaks.toArray());
}
use of com.intellij.util.containers.IntArrayList in project intellij-community by JetBrains.
the class ControlFlowUtil method internalDepthFirstSearch.
private static void internalDepthFirstSearch(final List<Instruction> instructions, final InstructionClientVisitor clientVisitor, int startOffset, int endOffset) {
final WalkThroughStack walkThroughStack = new WalkThroughStack(instructions.size() / 2);
walkThroughStack.push(startOffset);
// we can change instruction internal state here (e.g. CallInstruction.stack)
synchronized (instructions) {
final IntArrayList currentProcedureReturnOffsets = new IntArrayList();
ControlFlowInstructionVisitor getNextOffsetVisitor = new ControlFlowInstructionVisitor() {
@Override
public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) {
instruction.execute(offset + 1);
int newOffset = instruction.offset;
// 'procedure' pointed by call instruction should be processed regardless of whether it was already visited or not
// clear procedure text and return instructions aftewards
int i;
for (i = instruction.procBegin; i < clientVisitor.processedInstructions.length && (i < instruction.procEnd || i < instructions.size() && instructions.get(i) instanceof ReturnInstruction); i++) {
clientVisitor.processedInstructions[i] = false;
}
clientVisitor.procedureEntered(instruction.procBegin, i);
walkThroughStack.push(offset, newOffset);
walkThroughStack.push(newOffset);
currentProcedureReturnOffsets.add(offset + 1);
}
@Override
public void visitReturnInstruction(ReturnInstruction instruction, int offset, int nextOffset) {
int newOffset = instruction.execute(false);
if (newOffset != -1) {
walkThroughStack.push(offset, newOffset);
walkThroughStack.push(newOffset);
}
}
@Override
public void visitBranchingInstruction(BranchingInstruction instruction, int offset, int nextOffset) {
int newOffset = instruction.offset;
walkThroughStack.push(offset, newOffset);
walkThroughStack.push(newOffset);
}
@Override
public void visitConditionalBranchingInstruction(ConditionalBranchingInstruction instruction, int offset, int nextOffset) {
int newOffset = instruction.offset;
walkThroughStack.push(offset, newOffset);
walkThroughStack.push(offset, offset + 1);
walkThroughStack.push(newOffset);
walkThroughStack.push(offset + 1);
}
@Override
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
int newOffset = offset + 1;
walkThroughStack.push(offset, newOffset);
walkThroughStack.push(newOffset);
}
};
while (!walkThroughStack.isEmpty()) {
final int offset = walkThroughStack.peekOldOffset();
final int newOffset = walkThroughStack.popNewOffset();
if (offset >= endOffset) {
continue;
}
Instruction instruction = instructions.get(offset);
if (clientVisitor.processedInstructions[offset]) {
if (newOffset != -1) {
instruction.accept(clientVisitor, offset, newOffset);
}
// when traversing call instruction, we have traversed all procedure control flows, so pop return address
if (!currentProcedureReturnOffsets.isEmpty() && currentProcedureReturnOffsets.get(currentProcedureReturnOffsets.size() - 1) - 1 == offset) {
currentProcedureReturnOffsets.remove(currentProcedureReturnOffsets.size() - 1);
}
continue;
}
if (!currentProcedureReturnOffsets.isEmpty()) {
int returnOffset = currentProcedureReturnOffsets.get(currentProcedureReturnOffsets.size() - 1);
CallInstruction callInstruction = (CallInstruction) instructions.get(returnOffset - 1);
// we should push back to 'return offset' stack
synchronized (callInstruction.stack) {
if (callInstruction.procBegin <= offset && offset < callInstruction.procEnd + 2 && (callInstruction.stack.size() == 0 || callInstruction.stack.peekReturnOffset() != returnOffset)) {
callInstruction.stack.push(returnOffset, callInstruction);
}
}
}
clientVisitor.processedInstructions[offset] = true;
instruction.accept(getNextOffsetVisitor, offset, newOffset);
}
}
}
use of com.intellij.util.containers.IntArrayList in project intellij-community by JetBrains.
the class ControlFlowUtil method hasObservableThrowExitPoints.
/**
* Detect throw instructions which might affect observable control flow via side effects with local variables.
*
* The side effect of exception thrown occurs when a local variable is written in the try block, and then accessed
* in the finally section or in/after a catch section.
*
* Example:
* <pre>
* { // --- start of theOuterBlock ---
* Status status = STARTED;
* try { // --- start of theTryBlock ---
* status = PREPARING;
* doPrepare(); // may throw exception
* status = WORKING;
* doWork(); // may throw exception
* status = FINISHED;
* } // --- end of theTryBlock ---
* catch (Exception e) {
* LOG.error("Failed when " + status, e); // can get PREPARING or WORKING here
* }
* if (status == FINISHED) LOG.info("Finished"); // can get PREPARING or WORKING here in the case of exception
* } // --- end of theOuterBlock ---
* </pre>
* In the example above {@code hasObservableThrowExitPoints(theTryBlock) == true},
* because the resulting value of the "status" variable depends on the exceptions being thrown.
* In the same example {@code hasObservableThrowExitPoints(theOuterBlock) == false},
* because no outgoing variables here depend on the exceptions being thrown.
*/
public static boolean hasObservableThrowExitPoints(@NotNull final ControlFlow flow, final int flowStart, final int flowEnd, @NotNull PsiElement[] elements, @NotNull PsiElement enclosingCodeFragment) {
final List<Instruction> instructions = flow.getInstructions();
class Worker {
@NotNull
private Map<PsiVariable, IntArrayList> getWritesOffsets() {
final Map<PsiVariable, IntArrayList> writeOffsets = new THashMap<>();
for (int i = flowStart; i < flowEnd; i++) {
Instruction instruction = instructions.get(i);
if (instruction instanceof WriteVariableInstruction) {
final PsiVariable variable = ((WriteVariableInstruction) instruction).variable;
if (variable instanceof PsiLocalVariable || variable instanceof PsiParameter) {
IntArrayList offsets = writeOffsets.get(variable);
if (offsets == null)
writeOffsets.put(variable, offsets = new IntArrayList());
offsets.add(i);
}
}
}
LOG.debug("writeOffsets:", writeOffsets);
return writeOffsets;
}
@NotNull
private Map<PsiVariable, IntArrayList> getVisibleReadsOffsets(Map<PsiVariable, IntArrayList> writeOffsets, PsiCodeBlock tryBlock) {
final Map<PsiVariable, IntArrayList> visibleReadOffsets = new THashMap<>();
for (PsiVariable variable : writeOffsets.keySet()) {
if (!PsiTreeUtil.isAncestor(tryBlock, variable, true)) {
visibleReadOffsets.put(variable, new IntArrayList());
}
}
if (visibleReadOffsets.isEmpty())
return visibleReadOffsets;
for (int i = 0; i < instructions.size(); i++) {
final Instruction instruction = instructions.get(i);
if (instruction instanceof ReadVariableInstruction) {
final PsiVariable variable = ((ReadVariableInstruction) instruction).variable;
final IntArrayList readOffsets = visibleReadOffsets.get(variable);
if (readOffsets != null) {
readOffsets.add(i);
}
}
}
LOG.debug("visibleReadOffsets:", visibleReadOffsets);
return visibleReadOffsets;
}
@NotNull
private Map<PsiVariable, Set<PsiElement>> getReachableAfterWrite(Map<PsiVariable, IntArrayList> writeOffsets, Map<PsiVariable, IntArrayList> visibleReadOffsets) {
final Map<PsiVariable, Set<PsiElement>> afterWrite = new THashMap<>();
for (PsiVariable variable : visibleReadOffsets.keySet()) {
final Function<Integer, BitSet> calculator = getReachableInstructionsCalculator();
final BitSet collectedOffsets = new BitSet(flowEnd);
for (final int writeOffset : writeOffsets.get(variable).toArray()) {
LOG.assertTrue(writeOffset >= flowStart, "writeOffset");
final BitSet reachableOffsets = calculator.fun(writeOffset);
collectedOffsets.or(reachableOffsets);
}
Set<PsiElement> throwSources = afterWrite.get(variable);
if (throwSources == null)
afterWrite.put(variable, throwSources = new THashSet<>());
for (int i = flowStart; i < flowEnd; i++) {
if (collectedOffsets.get(i)) {
throwSources.add(flow.getElement(i));
}
}
final List<PsiElement> subordinates = new ArrayList<>();
for (PsiElement element : throwSources) {
if (throwSources.contains(element.getParent())) {
subordinates.add(element);
}
}
throwSources.removeAll(subordinates);
}
LOG.debug("afterWrite:", afterWrite);
return afterWrite;
}
@NotNull
private IntArrayList getCatchOrFinallyOffsets(List<PsiTryStatement> tryStatements, List<PsiClassType> thrownExceptions) {
final IntArrayList catchOrFinallyOffsets = new IntArrayList();
for (PsiTryStatement tryStatement : tryStatements) {
final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
if (finallyBlock != null) {
int offset = flow.getStartOffset(finallyBlock);
if (offset >= 0) {
// -2 is an adjustment for rethrow-after-finally
catchOrFinallyOffsets.add(offset - 2);
}
}
for (PsiCatchSection catchSection : tryStatement.getCatchSections()) {
final PsiCodeBlock catchBlock = catchSection.getCatchBlock();
final PsiParameter parameter = catchSection.getParameter();
if (catchBlock != null && parameter != null) {
for (PsiClassType throwType : thrownExceptions) {
if (isCaughtExceptionType(throwType, parameter.getType())) {
int offset = flow.getStartOffset(catchBlock);
if (offset >= 0) {
// -1 is an adjustment for catch block initialization
catchOrFinallyOffsets.add(offset - 1);
}
}
}
}
}
}
return catchOrFinallyOffsets;
}
private boolean isAnyReadOffsetReachableFrom(IntArrayList readOffsets, IntArrayList fromOffsets) {
if (readOffsets != null && !readOffsets.isEmpty()) {
final int[] readOffsetsArray = readOffsets.toArray();
for (int j = 0; j < fromOffsets.size(); j++) {
int fromOffset = fromOffsets.get(j);
if (areInstructionsReachable(flow, readOffsetsArray, fromOffset)) {
LOG.debug("reachableFromOffset:", fromOffset);
return true;
}
}
}
return false;
}
private Function<Integer, BitSet> getReachableInstructionsCalculator() {
final ControlFlowGraph graph = new ControlFlowGraph(flow.getSize()) {
@Override
void addArc(int offset, int nextOffset) {
nextOffset = promoteThroughGotoChain(flow, nextOffset);
if (nextOffset >= flowStart && nextOffset < flowEnd) {
super.addArc(offset, nextOffset);
}
}
};
graph.buildFrom(flow);
return startOffset -> {
BitSet visitedOffsets = new BitSet(flowEnd);
graph.depthFirstSearch(startOffset, visitedOffsets);
return visitedOffsets;
};
}
}
final Worker worker = new Worker();
final Map<PsiVariable, IntArrayList> writeOffsets = worker.getWritesOffsets();
if (writeOffsets.isEmpty())
return false;
final PsiElement commonParent = elements.length != 1 ? PsiTreeUtil.findCommonParent(elements) : elements[0].getParent();
final List<PsiTryStatement> tryStatements = collectTryStatementStack(commonParent, enclosingCodeFragment);
if (tryStatements.isEmpty())
return false;
final PsiCodeBlock tryBlock = tryStatements.get(0).getTryBlock();
if (tryBlock == null)
return false;
final Map<PsiVariable, IntArrayList> visibleReadOffsets = worker.getVisibleReadsOffsets(writeOffsets, tryBlock);
if (visibleReadOffsets.isEmpty())
return false;
final Map<PsiVariable, Set<PsiElement>> afterWrite = worker.getReachableAfterWrite(writeOffsets, visibleReadOffsets);
if (afterWrite.isEmpty())
return false;
for (Map.Entry<PsiVariable, Set<PsiElement>> entry : afterWrite.entrySet()) {
final PsiVariable variable = entry.getKey();
final PsiElement[] psiElements = entry.getValue().toArray(PsiElement.EMPTY_ARRAY);
final List<PsiClassType> thrownExceptions = ExceptionUtil.getThrownExceptions(psiElements);
if (!thrownExceptions.isEmpty()) {
final IntArrayList catchOrFinallyOffsets = worker.getCatchOrFinallyOffsets(tryStatements, thrownExceptions);
if (worker.isAnyReadOffsetReachableFrom(visibleReadOffsets.get(variable), catchOrFinallyOffsets)) {
return true;
}
}
}
return false;
}
use of com.intellij.util.containers.IntArrayList in project intellij-community by JetBrains.
the class TextPainter method drawString.
private boolean drawString(Graphics2D g, char[] text, int end, boolean lineStart, Point2D position, Rectangle2D clip, Color backColor, Color underscoredColor) {
boolean toContinue = true;
if (end >= mySegmentEnd) {
end = mySegmentEnd;
toContinue = false;
}
if (myOffset >= end)
return toContinue;
boolean isInClip = (getLineHeight(g) + position.getY() >= clip.getY()) && (position.getY() <= clip.getY() + clip.getHeight());
if (!isInClip)
return toContinue;
if (myPrintSettings.WRAP) {
double w = getTextSegmentWidth(text, myOffset, end - myOffset, position.getX(), g);
if (position.getX() + w > clip.getWidth()) {
IntArrayList breakOffsets = LineWrapper.calcBreakOffsets(text, myOffset, end, lineStart, position.getX(), clip.getWidth(), (t, start, count, x) -> getTextSegmentWidth(t, start, count, x, g));
for (int i = 0; i < breakOffsets.size(); i++) {
int breakOffset = breakOffsets.get(i);
drawTabbedString(g, text, breakOffset - myOffset, position, backColor, underscoredColor);
position.setLocation(0, position.getY() + getLineHeight(g));
if (position.getY() > clip.getY() + clip.getHeight() - getLineHeight(g)) {
return false;
}
}
}
}
drawTabbedString(g, text, end - myOffset, position, backColor, underscoredColor);
return toContinue;
}
use of com.intellij.util.containers.IntArrayList in project intellij-community by JetBrains.
the class TerminalBlock method findExitPoints.
Collection<PsiStatement> findExitPoints(ControlFlow controlFlow) {
int startOffset = controlFlow.getStartOffset(myStatements[0]);
int endOffset = controlFlow.getEndOffset(myStatements[myStatements.length - 1]);
if (startOffset < 0 || endOffset < 0)
return null;
return ControlFlowUtil.findExitPointsAndStatements(controlFlow, startOffset, endOffset, new IntArrayList(), PsiContinueStatement.class, PsiBreakStatement.class, PsiReturnStatement.class, PsiThrowStatement.class);
}
Aggregations